© Alejandro Serrano Mena 2019
Alejandro Serrano MenaPractical Haskellhttps://doi.org/10.1007/978-1-4842-4480-7_17

17. Looking Further

Alejandro Serrano Mena1 
(1)
Utrecht, The Netherlands
 

Congratulations for arriving at this point. Thus far in the book you’ve learned many of the features that Haskell provides in terms of language and libraries. The notions of functor, monad, and GADT should not be alien to you anymore. You’ve also seen how to design large applications in Haskell and how to manage databases and web applications. And above all, you’ve seen how a strong type system encourages a more systematic way of writing software and helps in reducing time spent in coding and maintenance.

As with any other language, a book cannot contain every single use of Haskell. Hackage and Stackage provide an enormous amount of functionality already packaged and should be your entry point for discovering the vast field of Haskell libraries. The book has just skimmed the surface of parallel and concurrent code libraries or web frameworks, to mention two examples of places where you may deepen your knowledge.

The Haskell language also has more surprises built into its compiler. In the previous chapter, I mentioned data type generic programming, and along with type-level programming, it can reduce even more boilerplate code for validation while increasing safety. The metaprogramming facility Template Haskell has been used extensively in the book, but how to create your own quasiquoters is beyond the scope of the book. Another place where you may expect new techniques to appear is in kind-level programming.

In conclusion, I hope this book doesn’t put an end to your journey in Haskell and functional programming but encourages you to use this language in your daily or hobby projects and to look at many of the available resources that are online. Thanks for reading!

Projects

In the previous chapter I have discussed application and library design in Haskell. In this section I’ll describe some projects to put that advice in practice, along with the design considerations you might look at. From those considerations, you can infer patterns to apply in your own designs.

In addition, you can see these projects as extra, more comprehensive exercises for practicing your Haskell skills. In each of these cases, the solution is more open-ended than for the tasks suggested in the rest of the book. One hint for all the cases is to search libraries in Hackage or Stackage that may help solving the problem at hand.

Data Mining Library

Part 2 of the book was devoted to developing two data-mining algorithms: K-means for clustering and Apriori for association rules learning. This first project entails turning the code you already have into a full-fledged data mining library, with support for different algorithms for each problem.

Even if you consider only one problem, such as clustering, it’s interesting to look at the commonalities of each algorithm solving that task. For example, data can come from different sources (a list, a conduit source, a database query), but you should provide a common interface to all of them to make the algorithms independent of that choice. In Haskell, this level of abstraction is obtained via type classes. Here, you may create a ClusteringDataSource one.

A good library includes not only data types, functions, and type classes but also an exploration of the laws and properties that the user should count on. For data types, it’s useful to consider whether a given type is an instance of one of the common abstractions in the Haskell Platform. Here are some examples:
  • Many data types are composed in a way that allows you to generate a new value from the other two. The Monoid type class handles this case. Making your type a monoid can guide you into thinking whether a neutral element would be useful in your case.

  • Other types fit more into the container-like intuition. In those cases, try to instantiate the Functor, Foldable, and Traversable type classes.

  • Those data types that entail some kind of computational context, or in some way a description of a set of actions to take, fit well into the Applicative and Monad abstractions. Additionally, if your type allows choice, Alternative and MonadPlus may help.

Figure 17-1 shows most of these type classes, along with their parent-child relation. Alternative is not nominally a superclass of MonadPlus (it does not appear in its declaration), but this is more an historical accident that an explicit design choice in the base libraries.
../images/316945_2_En_17_Chapter/316945_2_En_17_Fig1_HTML.png
Figure 17-1

Important type classes in the Haskell Platform

When writing instances of these well-known abstractions, you should abide by the rules they come with, even though those rules are not checked by the compiler. Users of your library would be surprised to find an instance of Functor that doesn’t satisfy that fmap id = id, for example. The general guideline is the no-surprises rule, sometimes referred to as the principle of least astonishment: make your library do what users expect from it or otherwise document the behavior prominently. For help in ensuring that rules are always kept correct, you may use formal verification or QuickCheck.

One extra consideration you should make is for performance. Be aware that your previous ideas about code optimization may not be useful in Haskell, where code is evaluated lazily. I’ve already discussed the GHC profiler and the Criterion package, which are valuable in spotting performance deficiencies and finding the cause. If you’re interested in complexity analysis of Haskell code, you should read the book Purely Functional Data Structures by Chris Okasaki (Cambridge University Press, 1999).

Write Applications as Libraries

Your software design can greatly benefit from thinking of your application as a core library that is later consumed by a front-end application. Cabal embodies this strategy by allowing a stanza for the library part and referring to it in an executable.

The reason for this advice is this: knowing that another developer may consume your code, one is usually more careful when refining the abstractions that will be exposed. Also, it makes it easier to refactor and reuse the core parts of your project, instead of entangling them with the interaction-specific code.

Store Network Client

In the Store internal network, you need to create a server and clients that can be used by workers. This kind of application uses a variety of functionality: keeping an internal consistent state, communicating through a network, logging problematic scenarios, and so on. As you saw in Chapter 7, such functionality is a perfect task for a monad stack, which brings together the features you need from different pieces. In this case, a possible choice of monad transformers may be as follows:
  • IO and ConduitM for managing the network connections, or Process if you prefer to use the actor model.

  • SqlPersistT for database access, via the Persistent network.

  • STM can be used to keep the internal state consistent against concurrent modifications. If you follow the actor model, a simpler StateT transformer can keep the state.

  • ReaderT could keep track of different configuration options, like the port you’re using for communication or the database connection options.

  • The logging functionality can be implemented via WriterT.

However, you shouldn’t just create a big monad stack and pass it everywhere. As discussed earlier, you should try to keep your code as polymorphic as possible. If instead of ReaderT r (SqlPersistT IO) you program against MonadReader, you can grow or shrink your monad stack at will during the development of your application. It is not uncommon for you decide to add a new layer during the development of the application. One function that queries the database and logs some information should have a signature like this:
getClientInformation :: (PersistQuery m, MonadWriter String m)
                     => ClientId -> m [Client]
instead of the more specific one shown here:
getClientInformation :: ClientId -> WriterT String (SqlPersistM [Client])

Developing against monad classes also helps in separating the different concerns of your application. In this sense, monad classes resemble aspect-oriented programming, where you define each part of your application dealing with some different feature separately and then mix them together. The signature, via the list of monad classes needed to implement some function, defines exactly which functionality is needed from the context. Giving a specific incarnation via a monad stack is the equivalent of choosing the implementation.

Administration Interface and Tetris

An administration interface for the Store is a way to manage products and stock in the system and to modify and decline purchases from clients. Tetris, on the other hand, is the perfect game for entertaining time travelers while the time machines operate their magic. What those two applications have in common is that the set of actions you can perform on them are limited. In the administration interface, you can access only certain functionality depending on your security level; in Tetris, you can move the current piece in only three directions or rotate it.

In different parts of the book I have discussed how a domain-specific language (DSL) can help you constrain the value that can be represented by a data type. In that case, the aim was to restrict the data that can be expressed and processed by the application. But you can follow the same idea and create a domain-specific language for the actions that your application is able to take. There are several examples of this pattern: Esqueleto embodies a SQL-like language for expressing database queries, attoparsec has a language based on the Applicative interface for describing parsers, and Spock uses its own DSL to specify routes in your web application. The DSL approach has two main advantages:
  1. 1.

    It restricts what you can do in a certain context. It’s not the same to have a signature using the IO type, which may use unrestricted side effects, as it is to have a specific monad for your application that allows only network and database connections. The type system can be used to ensure many more invariants.

     
  2. 2.

    If you use a deep embedding for your actions, you can provide several interpretations for the same DSL. This can be useful in testing. For example, if your DSL is used for network communication, you can provide an interpretation that fakes a network conversation between two peers.

     
Within Haskell libraries and applications, you can find two different ways in which domain-specific languages for actions are implemented. You have seen examples of both throughout the book.
  1. 1.

    One possibility is developing a combinator library . In this case, you specify a set of basic constructs and a series of functions that combines those basic blocks. This is the approach taken by the attoparsec library for building parsers.

     
  2. 2.

    The other option is rolling your own monad, as described in the previous chapter. A successful example of building an application around a monad is XMonad, a window manager written in Haskell; but you can also find this pattern in Spock, which defines the ActionT monad transformer, and Esqueleto, where queries are expressed as values of the SqlQuery monad.

     

One important consideration is whether you need to inspect the structure of your computation before executing it. For example, you might want to perform optimizations in the monadic code. In that case, rolling your own monad in the form of a free or operational monad usually gives you more hooks to perform that work, in comparison to combinator libraries.

Additional Haskell Resources

Haskell has an active community on Internet, as the enormous database of packages and the level of activity in forums and mailing lists acknowledges. The places where you can look for more information include the following:
  • Haskell’s main page, at www.haskell.org , contains pointers to many tutorials and resources. It’s built as a wiki, and its users are always adding new information that may be interesting for Haskell developers.

  • The Monad Reader was a regular magazine with articles practical or enlightening to developers. Unfortunately, there are no new issues since 2015, but you can still check all the published issues at themonadreader.wordpress.com .

  • If you want to look at examples of elegant and instructive functional code, you should look at the “ Functional Pearls section on Haskell’s wiki, at wiki.haskell.org / Research_papers/Functional_pearls.

  • To stay tuned with the latest news of Haskell, GHC, and Hackage, you can subscribe to the Planet Haskell feed aggregator at planet.haskell.org . You’ll see that there are quite a number of bloggers speaking about Haskell.

  • Another way to keep yourself updated is the Haskell subreddit at www.reddit.com/r/haskell/ . Reddit allows you to comment about articles; many interesting discussions start in this way.

  • The Haskell community maintains a mailing list called Café, at mail.haskell.org/mailman/listinfo/haskell-cafe , which is a space where many discussions about the language and libraries take place. If you look at the archives, you’ll notice that the list welcomes both newcomers and experienced developers. If you have a question, just ask, and you’ll get a gentle response.

  • There is another mailing list more focused on beginners at mail.haskell.org/mailman/listinfo/beginners .

  • If you prefer more direct communication, you can use IRC. The #haskell channel at irc.freenode.net is usually filled with people talking about the language.

Other Functional Languages

Most of the concepts and ideas in this book are applicable to many other programming languages. One of the closest set of languages is the ML family, which includes OCaml and F# (this language integrates into Microsoft’s .NET Framework, allowing easy interoperability with software written in C#). The main difference with Haskell is the use of strictness instead of laziness. Languages from the Lisp family, like Racket or Clojure, also embody functional concepts. Some languages mix functional concepts with other paradigms. One interesting example is Scala, which puts under the same umbrella functional and object-oriented programming inside the Java platform. Newer languages, such as Swift (for the iOS platform) and Kotlin (for the Java platform) also embody many functional ideas.

In this book I’ve mentioned several places where Haskell abstractions are directly applicable in other languages. The monad concept lies behind the LINQ libraries in C#, the for expressions in Scala, and the computation expressions in F#. Libraries for Software Transactional Memory have been put into many other languages. Parser combinators are becoming increasingly used for treating text data. As you can see, the intuition gained from working on Haskell can be reused in many other scenarios.

Functional Libraries in Other Languages

Not many languages are so focused on functional programming as Haskell is. For that reason, their base libraries may lack some types and functions that you take for granted in Haskell. Fortunately, there are many open source libraries filling those gaps:
  • Scalaz and Cats ( typelevel.org ) are two ecosystems for functional and category-theory-oriented programming in Scala. Both define monoids, functors, monads, and many other type classes (traits in Scala’s lingo).

  • Arrow (arrow-kt.io) describes itself as a “functional companion to Kotlin’s standard library.” Bow (bow-swift.io) is a similar library but for Swift.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.134.151