Spotlight: Rebecca Skinner (Author) Interview and AMA!

Author Spotlight
Rebecca Skinner
@RebeccaSkinner

Welcome to our latest author spotlight, where we sit down with Rebecca Skinner, author of Effective Haskell. This book puts the power of Haskell to work in your programs, learning from an engineer who uses Haskell daily to get practical work done efficiently. Discover how to realize the benefits of a pure functional language, like protecting your code from side effects. Learn to employ advanced programming concepts to solve real-world problems. Don’t just learn the syntax, but dive deeply into Haskell as you build efficient, well-tested programs.

This is also an AMA. Everyone commenting or asking a question will automatically be entered into our draw to win a copy of Rebecca’s book!

9 Likes

Please introduce yourself

I’m Rebecca Skinner, and I’m the author of Effective Haskell. I’ve been writing Haskell for over ten years now. For the last two and a half years or so I’ve been volunteering on the Haskell.org committee, which is the group that helps manage the Haskell.org website and some of the Haskell infrastructure. I suppose you could say that I’m really enthusiastic about Haskell.

For people who are not familiar with the Haskell language, how would you introduce it to them?

Haskell is a pure functional programming language that puts a strong emphasis on letting programmers build precise abstractions to represent particular problem domains. Haskell has a lightweight and flexible syntax. Combined with lazy evaluation and a strong type system, Haskell is particularly well suited for building APIs and designing systems that need to capture the complexities of different technical or business domains and present them to users in a way that is both easy to use and hard to misuse.

You started off by saying that Haskell is a pure functional language. So could you get into that a little bit and talk about how that affects coding?

I think most programmers these days have exposure to functional programming. Newer languages like Rust, Swift, and Kotlin have taken a lot of inspiration from functional languages, including Haskell. There’s also a pretty big contingent of people in the JavaScript and TypeScript ecosystem who are enthusiastic about functional programming. Even C++ and Java have started to get more features inspired by functional programming. I think the most surprising thing when you move from an object-oriented or multi-paradigm language to a functional programming language is how much working in a functional language affects the way that you think about your program at the architectural level.

Although functional programming has a reputation for being a very high-level abstract way of thinking about programs, in a lot of ways it leads us to a more concrete and direct way of thinking about architecture and how we solve problems. In an object-oriented language, programming can be very indirect. If you want some specific output, you need to call a method on one object that sends a message to another object and so on. Abstraction in an object-oriented language generally involves adding more layers of indirection through objects and interfaces. Functional programming frequently makes the abstraction more direct because there’s a focus on composing smaller pieces of functionality on the fly. You can start to see this when you use functional features in non-functional languages. For example, using higher-order functions like map encourages you to directly write a lot of small functions that do one particular transformation on an element and then pass them to the map function. In a more traditional OO language, where composition is harder, you’re often faced with the choice to predict all of the different operations you might want to do on your data ahead of time, or else you need to add design patterns like factories and adapters that work around the lack of composition. Those design patterns can make it hard to follow what your program is doing.

While most programmers have had some exposure to the idea of functional programming in their languages thanks to the new features that are getting added, people are often a bit less comfortable with the idea of pure functional programming. In a pure functional language, all of our values are immutable and we avoid any arbitrary external side effects, like printing things to the screen or talking to the network. At first this can seem really limiting, because it focuses on all of the things that we can’t do, but in practice it turns out to be extraordinarily useful when we are working in a functional language because it means that we can combine code freely without ever having to worry about accidental or unintended side effects. We’re also not totally prohibited from having side effects when we need them, they just aren’t the default. In Haskell we already have a lot of really nice ways to combine smaller parts of our program into a larger program, and we reuse these same capabilities to let us manage side effects. In Haskell, if you want to do something like open a file or make an HTTP request, you create an IO action that will do that thing for you when the program runs. You can combine all of these little IO actions into one big one, and that big IO action that you created is what does all of the work when you actually run your program.

Could you discuss how Haskell APIs promote safety?

A Haskell API really is just describing and presenting a surface for someone to interact with our code. The difference is that Haskell gives us a lot of flexibility so that we can be very precise about how we describe that interface, and it lets us limit the interactions so that people can’t use our program incorrectly. Pure functional programming is a great tool for this, because we can design an API where we can very precisely control what inputs a user needs to accept and what outputs they need to return. When we know that code the user is passing in won’t have any side effects, it makes it easier to have confidence in how our code will be used. For example, if I’m building an API to interact with a system that requires authentication and has security policies in place, I can ask the user to pass me pure functions, and I know that any private information I pass into those functions won’t leak out, because the function can’t have any side effects.

How can Haskell help you with asynchronous code?

There are a couple of different ways that we deal with asynchronous programming In Haskell. The async library is one way we can do asynchronous programming, and it’s a great example of the point I was making earlier about how Haskell’s purity and type system can help us design APIs that make it easier to do resource management. The async library lets us write asynchronous code without worrying about leaking resources and forgetting to wait on threads, and it does it by treating the threads as the resource that the library is managing. When we use the library, we can say, ā€œHey, run this code asynchronously while you also run this other code,ā€ and the library will create a new thread and pass it in as an argument to your function. You can do anything you might need to do, like sending a signal to the thread or canceling it, but it’s only in scope while your function is running and it will be cleaned up for you automatically.

The STM library is another example of a really great library that makes it easy for us to deal with asynchronous code in a way that feels familiar to anyone who has done async programming in other languages, but we get some really nice extra properties from STM compared to the usual synchronization primitives. STM stands for ā€œsoftware-transactional memoryā€ and it makes it easy to write small individual parts of a program that each deal with some concurrency, and we can compose those effectively into a larger functions without any individual part of the application needing to know how the other parts of the program are handling their own concurrency.

Really, being able to compose functions is where I think Haskell shines whether those functions are directly needing to think about concurrency or not. If we think about a language like JavaScript that handles asynchronous code with promises, you see a pattern that comes up repeatedly where you need to combine promises into a bigger promise so that you can run more of the code asynchronously. In practice you can end up with a pretty large asynchronous program that gets run in the end and it’s just going to, for example, call one API and then wait for the response and pass that response to the next API, wait for that response, and so on. It turns out that that is almost exactly the same problem you have when you think, ā€œOkay, well, I’m reading a file from disk, and then I’m writing some of the file to the screen, and then I’m going to wait for user input.ā€ There are other examples that aren’t quite as obviously the same until you think about it: I have this function that might fail, but if it doesn’t, I want to take its output and pass it into this next function that might also fail, or I have this list of values and I want to take each one and pass it to a function that will return its own list.

When I talk about the ways that Haskell’s type system can help us write nicer APIs, this is another great example. Thanks to higher-kinded types, we can define these really broadly useful libraries that work with all different sorts of things that we might want to combine like this. We call them Applicatives and Monads. Haskell itself also has some special syntax called ā€œdo notationā€ that makes it easier to work with Monads. The underlying features that Haskell has for dealing with asynchronous programming are nice, but the fact that we can reuse so many useful libraries when we’re working with asynchronous code is an even bigger benefit.

Is this similar to the notion of generics or protocols in other languages?

Yeah, exactly. If we’re talking about generics in a language like Java, we have generics that let us write a value like ā€œa list of type Tā€ where we can make T a string or number or another list. We can do that in Haskell too, but we can go a bit further and instead of being specific about having a list, we can make that generic as well. You can kind of get there with interfaces, but the higher-kinded types give you a lot of expressive power that you just don’t have in other languages.

How do you interact from Haskell with, for example, a C library or Java libraries, or just external libraries, so that you can take advantage of that code? Either directly, or by building some interop bridge?

Haskell has very good support for interoperating with the C ABIs. It’s pretty straightforward to use C libraries from Haskell, and a lot of other languages support the C calling convention, so we can generally interoperate with most other natively compiled languages like C++, Rust, and Go either directly or with only some minor modifications or a small shim. We can go the other direction too, and create native libraries from Haskell that can be used in other languages. There are also tools like C2HS that let you automatically generate language bindings for Haskell from larger C applications, and there are a number of projects that let you compile Haskell code into other languages.

How can people keep track of what you’re doing and follow you?

My website is https://rebeccaskinner.net, and I’m often active on Twitter at cercerilla. For anyone who is reading Effective Haskell, I’m also active on the DevTalk forum, where I love getting feedback and answering questions about the book.

Thank you for taking the time to speak with us. This has been great!

8 Likes

Drop any questions you have for Rebecca into this thread. Everyone commenting or asking a question will automatically be entered into a drawing to win a copy of her book!

If you don’t want to wait, you can pick up your copy of Effective Haskell today!

Don’t forget you get 35% off with the coupon code devtalk.com!

3 Likes

Hello, I came across this article on reddit. Ty for sharing.

I’m a haskell newbie and there is this question about functional programming vs imperative that I’ve read a lot about but I’m still not fully convinced of the answers because I can’t explain it myself to someone else.

Pattern matching vs switch ? What are the real benefit of it ? What would be your take on this one ?

3 Likes

That’s a great question. There’s a lot of overlap, between the two, but there are some things you can do with a switch statement that you can’t do with pattern matching, and vice versa.

Let’s look at an example of where they overlap. One common way that people use switch statements is to pick a choice from a set of potential options. For example, if you were writing a program to display a list of files, you might want to match specific file extensions and show a more human readable name for the file type. In a language with switch statements you might say (in C-like pseudo-code):

string get_file_type_description(string file_name) {
string extension = get_file_extension(file_name);  
switch (extension) {
    case "png":
      return "Image Fiile";
    case "txt":
      return "Plain text file";
    default:
      return (extension + " file");

In this example, we’re matching each branch of the switch statement with an exact value, and returning a value based on that. In Haskell we can do the same thing with pattern matching. The usual way would be using a case expression, which looks pretty similar to switch:

getFileDescription :: FilePath -> String
getFileDescription fileName =
  case getFileExtension fileName of
    "png" -> "Image File"
    "txt"   -> "Plain text file"
    extension -> extension ++ " file"

The most notable difference in this example is that when we’re using pattern matching, we don’t have keywords like case and default. Instead, each branch starts with the pattern we want to match against (in this case, the exact string we want to match on). Instead of a default case, we just provide a variable that will end up holding whatever value is there if we didn’t match one of the other branches.

It’s a little surprising that the very common use case for switch statements and pattern matching with case overlap so much, because in a lot of other ways they offer really different features. One of the main selling points of pattern matching is that it allows you to quickly access data that has a fairly complicated ā€œshapeā€. Let’s look at another common example of pattern matching with case: handling command line arguments.

In a larger application you might use a library to deal with command line arguments, but for short programs with only a couple of options, you might want to do it manually. Let’s say that you’re building a program to copy a file. You want to support a couple of different options for copying:

First, if the user passes in the --help flag, you want to display a help message. Otherwise, they should pass in a source file, a destination file, and an option number of bytes to copy We can use pattern matching to make the process a lot easier:

getProgramOptions :: [String] -> Either String ProgramOptions
getProgramOptions commandLineArgs =
  case commandLineArgs of
    [inputFile, outputFile] -> 
      makeProgramOptions inputFile outputFile CopyAllData
    [inputFile, outputFile, amount] ->
      makeProgramOptions inputFile outputFile (CopySomeData amount)
   _ -> showHelpText

In this example we’re using pattern matching to identify how many arguments were passed in, and to bind the value of those arguments to particular variables like inputFile and outputFile (the _ at the end says to match anything and ignore the values, like default). This is really convenient because it lets us combine the common concerns of ā€œcheck how many arguments were passed inā€ and ā€œassign the arguments to a variableā€ into a single expression.

Another benefit of pattern matching is that you can use it in more places than you would want to use an case expression. For example, you can commonly see pattern matching being used with functions. This function to recursively add up the numbers in a list is one example:

addNumbers [] = 0
addNumbers (thisNumber:rest) = thisNumber + addNumbers rest

In the first function definition, we match on the case where we have an empty list and return a value of 0. The second definition matches the first element of the list and the rest of the list into two parts. It’s safe, because we’ve already check for empty lists in the earlier version of the function.

Another case where pattern matching is useful is when you are defining values. For example, we can get each of the elements out of a tuple with pattern matching in a way that looks awfully similar to multi-return in languages like Go:

returnTwoNumbers = (5,7)
sumTwoNumbers =
  let (x,y) = returnTwoNumbers
  in x + y

Here, we’re using a pattern to make it easier for us to get at each of the values inside of the tuple. Without pattern matching we’d have to say:

sumTwoNumbers =
  let numbers = returnTwoNumbers
  in fst numbers + snd numbers

Of course, there are some situations where switch can do things that pattern matching doesn’t do. For example, some languages let you embed an arbitrary expression in your switch statements:

string file_size_units(int bytes) {
  switch(bytes)
    case (bytes > 1024):
      return "kb"
    case (bytes > 1024 * 1024)
      return "mb"
    case (bytes > 1024 * 1024 * 1024)
      return "gb"
    default:
      return "bytes"
}

There’s not a good way to use pattern matching for this in Haskell, but we can use a different feature, guard clauses to get the same ability:

fileSizeUnits bytes
  | bytes > 1024 = "kb"
  | bytes > 1024 * 1024 = "mb"
  | bytes > 1024 * 1024 * 1024 = "gb"
  | otherwise = "bytes"

Anyway, this response has gotten pretty long, but I hope it helps!

7 Likes

Another great spotlight :023:

Firstly, congrats on the book Rebecca! I remember you were active in the Rails chat room when I first got into it about a decade ago so it’s really nice to see you’ve finally got around to writing a book! :smiley:

I have a couple of questions for you!

I’ve often heard people say that Haskell is very ā€˜academic’ and ā€˜hard to learn’, do you have any thoughts on that and what learning resources would you recommend a newbie before them starting your book?

My other question is about the future - what do you think we’ll see in the dev (or tech) world 10 to 50 years from now? :upside_down_face:

2 Likes

Great questions!

I think ā€œhaskell is academicā€ and ā€œhaskell is hard to learnā€ are both true, but neither of them are true in quite the way that people think. In reality, I think Haskell is a lot more accessible than someone would expect going into a language that has a reputation for being a hard-to-learn academic language.

To start with, it’s true that Haskell is an academic language in the sense that it’s one of the languages academics often use for programming language research. Haskell’s both a great mature language to target when you are exploring new ideas for programming languages, and it’s a great choice for an implementation language when you are trying to write a new language to explore your ideas. In my opinion, this is a huge benefit to Haskell overall, because it means that Haskell often gets ā€œfirst dibsā€ on new ideas for how to make programming better.

That said, while academics are a notable part of the Haskell community, they are still just one part of it. There are professionals who are interested in Haskell as a language for shipping products, and hobbyists who are interested in Haskell for personal learning and exploration. I think it makes for a much stronger community, but it does mean that you might accidentally find yourself wandering into an academic discussion that you weren’t expecting.

I think this is also a bit related to the difficulty of learning Haskell. I don’t think it serves anyone to pretend that Haskell is easy to learn. It can be a challenging language to learn, but I think it’s mostly unrelated to the academic uses of Haskell.

The biggest problem I see people run into when trying to learn Haskell is the ā€œbootstrapping problemā€. There’s some really fundamental stuff in Haskell that it’s hard to learn without already knowing it. A lot of ā€œTo understand A you need to learn B, but to understand B you need to learn Aā€. I’d say that’s been one of the hardest parts of writing Effective Haskell. Early on, I made a decision to, as much as possible, avoid introducing things that I wasn’t going to explain yet, and to avoid misleading analogies and useful lies to get around this problem. I think that I’ve managed to do that, but it’s a big part of why the book has taken me so long to write (and one of the reasons I’m so proud of it).

I guess that feeds into the other part of your question- what resources would I recommend before starting my book? My answer is that I really hope you don’t need any specific resources! I’ve tried really hard to make the book both useful to people who have a little Haskell experience, but still approachable by people who have done programming but have never worked with a functional or strongly typed language.

My other question is about the future - what do you think we’ll see in the dev (or tech) world 10 to 50 years from now?

I think we’ll continue to see a broad trend toward pure functional programming as a core feature for all mainstream languages, and I expect Haskell will continue to both grow in it’s own right and continue to influence other languages as they start to look for how to build functional programming features. I think in the next 10 years we’re going to see both linear types and dependent typing become, if not mainstream, at as common in industrial software development as things like higher ranked types and type families are today. It wouldn’t surprise me at all if at least one of the current mainstream languages adopts something like Rust’s affine types in some capacity or another. Maybe two languages, but nobody will understand how it works in C++.

As languages like Haskell, Rust, and C++ gain more features, I think we’re also going to see a surge in smaller and simpler domain specific languages that address some specific areas where the industry is current struggling with intractable complexity. I expect these languages will almost all be pure functional languages, and I’d guess some of them will be strongly typed, but probably not all. I’d guess that we’ll continue to see nix gain some momentum and it might end up being one of the leading examples of this kind of DSL. I’m not sure if Dhall itself will see much adoption, but I think it’s another example of the kind of thing we’ll see more of.

5 Likes

Thanks for the in-depth reply Rebecca! It’s good to know that Effective Haskell doesn’t require any previous knowledge about Haskell (maybe we can get a book club going for it one day!)

I have another question :see_no_evil:

Which areas (other than academia) do you think Haskell is commonly used, and which areas do you think it could (should?) be used?

2 Likes

I’d love to get a book club going one day. I’ve had a mind to start a twitch stream to work through the examples and answer questions about the book, but that will have to wait until I’m finished writing the rest of the chapters!

I see Haskell used in quite a few different places in industry. I think it’s a great choice for every day backend CRUD web applications, and I know it’s been used from time to time on the frontend with ghcjs.

A couple of the places I’ve seen Haskell work really well are data engineering and in-house languages.

I think the in-house languages story is pretty widely know these days. Haskell is good at building languages, and you see some companies use it for embedded DSLs (meta for example is well known for using a Haskell DSL for some of their platform) and external DSLs (I worked at a large retailer where we used Haskell to build an external DSL and interpreter that let logistics and supply chain engineers model our supply chain network).

I don’t see data engineering brought up as often, but Haskell is a fantastic tool for building data pipelines. Haskell’s type system is really helpful when you are translating between different representations of your data and you want to have high assurances that you aren’t accidentally mangling the data or introducing some incorrect transformations. Haskell also had really good library support for streaming, which makes working with large datasets easier, and pure functional programming in general lends itself well to horizontal scalability- something you really need when dealing with large data pipelines.

In principal I think Haskell could also be a good fit for machine learning and general data science work, but in practice it doesn’t seem to be there yet.

Overall I think Haskell is mature enough to be reasonably considered for a lot of general purpose software work. The library ecosystem is strong enough to support most work that isn’t highly specialized. The biggest challenges to adopting Haskell right now are, I think, concerns over training and being able to hire enough Haskell developers (something that’s improving every year, and one big motivator for my book), and general organizational buy-in. A lot of companies are averse to picking anything they see as non-mainstream. I do think over time we can push the needle on that too, the more other languages adopt features from Haskell and people see benefits to them, the more open organizations will be to Haskell itself.

5 Likes

Ty for this detailed answer next time I’m asked about this I will be more convincing for sure :grin:.

I have another question about the Haskell eco-system maturity. In the data/machine learning engineering field, we focus a lot on JVM languages and python due to the major tools and framework provided in those eco-system.

From my understanding, it’s the current meta of development to use those tools and framework to do stuff in the real world.

As for Haskell, myself being a newbe, I have not a single idea of what it looks like in real life. What do we do when we want to orchestrate job ? Test it ? Deploy it ? Multi threading ? Do we actually have some tools at our disposal ? Or maybe Haskell is so great that we don’t need any of those :grinning: (no sarcasm intended) and therefore they don’t exist.

(Just noticed that your answer just above answers my last question :grimacing: sorry for the double question)

2 Likes

No trouble at all! To expand a bit on some things you mentioned in your question that I didn’t touch on as much earlier:

Testing:

Haskell has a pretty good testing story in my experience. I personally tend to use hspec for spec style testing, and either quickcheck or hedgehog for property based testing. There are quite a few other testing frameworks out there to either help with writing tests or to offer different approaches to testing.

Deployment:

Since Haskell is a compiled language, deployment is pretty much the same as any other compiled language. Compile, copy the binaries, install dependencies if necessary, and run the application. A lot of people use Haskell inside of docker containers. I don’t touch on this in Effective Haskell at all, because it would have been too large of a digression from Haskell itself, but I prefer to build and deploy my applications using nix. It has a pretty steep learning curve, but once you get past the learning curve, it’s really useful for building and deploying things, and the Haskell support is particularly good.

Multithreading:

Haskell does have really great support for multithreading, including a great tool called threadscope that lets you understand what’s happening in multithreaded code. Another great example of multithreading support in Haskell is the STM library, which gives you some really amazing safe primitives for building concurrent applications. At it’s best, I think Haskell’s concurrency story gives you all of the ā€œfearless concurrencyā€ of Rust alongside the ease-of-use of channel based communication (like Go’s channels, for example).

I hope that helps answer even more of your question!

5 Likes

Thank you so much for writing this book! I very much look forward to reading it.

Haskell-specific questions:

  • What is your opinion on TemplateHaskell? What do you think of it compared to other approaches of metaprogramming?
  • Which GHC extensions do you virtually always enable, and which ones do you think should be left alone?
  • What do you see as the biggest foot-gun for people starting out with Haskell?

Other questions:

  • What is your favorite animal?
  • What is your favorite beverage?
  • What do you think about hammocks?
2 Likes

Wow. Nice discussion already. Really enjoyed reading all those questions and answers.

Besides my interest in Haskell (I’m a beginner),
Iā€˜m using nix for some time now.

You mentioned nix is your preferred tool to build and deploy Haskell projects.
Are you going to provide some examples in the book how to you use nix to achieve such things or at least some guidance for the curious? :smiley:

2 Likes

I love nix, but I had to make the hard choice to leave it out of the book. All of the examples are things you can definitely use nix with, and in fact I’ll likely include some nix code in the online source when the book is released. Unfortunately it would have been too much of a digression from the focus of the book to include it, and would have risked alienating readers who aren’t familiar with nix.

4 Likes

These are great questions!

What is your opinion on TemplateHaskell? What do you think of it compared to other approaches of metaprogramming?

I don’t use template haskell much. As a general rule, I prefer generic and type level programming over template haskell when possible, since they feel more natural to me when I’m writing code. I also find it a little easier to debug personally. I wouldn’t go so far as telling people not to use template haskell, but I did end up leaving it out of the book- mostly due to space constraints.

Which GHC extensions do you virtually always enable, and which ones do you think should be left alone?

I’d say OverloadedStrings is probably the extension I use most of all, since I just very rarely use String in most of my code. For my hobby projects I tend to either do a lot of type level programming, which comes with a large list of extensions, or focus on very performance oriented code- in those cases I don’t use many extensions.

For the hot take part of this answer, I personally avoid RecordDot notation, because I just really don’t like the aesthetics of it. I also tend to avoid LambdaCase because fairly often I end up refactoring my code and having to remove the lambda case anyway- and I don’t think it actually looks that much nicer. I use RecordWildcards more than I should- they make writing code really convenient, but I’ll admit that it can come at something of a maintenance burden later.

What do you see as the biggest foot-gun for people starting out with Haskell?

I think the biggest footgun of all is the lure of over-abstraction. Haskell offers up a lot of power, and sometimes people get really keen on using some new idea and try to shoehorn it into places they shouldn’t. As a learning exercise this can be a great way to get a feeling for an idea and when to use it, but it does end up being a problem when you are trying to get stuff done. I think it’s really helpful for people learning things in haskell to be mindful of when they are experimenting for the sake of learning, and when they are trying to be productive.

A more concrete footgun I think is laziness. A lot of Haskell developers think laziness was a mistake- I disagree. I think it’s a really fundamental and great feature of Haskell, but it can still be a footgun early on. Once you learn how to think about laziness, and how it works, then it’s not hard to work with, but until then it’s tricky. I make a point of covering laziness extensively as several points in the book to try to help save some of my readers toes :sweat_smile:

What is your favorite animal?

My favorite specific animal is my parrot, George, who makes several appearances in the book. I also love goats because they are just agents of pure chaos and I really feel that deeply in my soul. I’ve often suggested Haskell should adopt a Sloth as it’s mascot.

What is your favorite beverage?

I drink a lot of coffee (black) and LaCroix (beach plum and peach pear). I don’t drink alcohol often, but there is a wine bar in Minneapolis that I know of called ā€œHaskell’s wine barā€ and so I feel like I ought to at least try to like wine.

What do you think about hammocks?

They seem dangerous.

4 Likes

Wow, what a really fantastic overview … I need to learn functional programming! :nerd_face::+1:

3 Likes

Hi @RebeccaSkinner , I have attempted learning Haskell several times without success and I left several comments on my failed journey with the language. What would be your recommendations to a newcomer like myself that’s interested in leveraging Haskell to build applications and solve problems in the real world (i.e. healthcare and education)? Also, would your book be a good pragmatic resource for learning Haskell? BTW, I have worked as a software engineer for 10+ years using languages like C/C++, Java, Swift, Ruby, Elm, and most recently Elixir. I look forward to your feedback and huge congrats on the book.

2 Likes

Hi @RebeccaSkinner

Thanks for the interview and detailed responses to questions.

I’ve played around with Scala, Haskell and Clojure over the years, but I’ve never found an employer who allowed use of either. Every time I think or read about writing in pure functional languages, I’m reminded of both the joys and frustrations. I’ve worked pretty much exclusively in OO languages and so when I look at functional languages, they always feel restrictive at first. But I also found things that were so freeing and powerful. Pattern matching, easy composition and a simplicity that seems hidden initially because the thinking is so different.

While I still work in what would certainly be termed OO languages - mostly C# - I’ve been able to take some of that thinking into what I do day to day. I find myself thinking a lot about composing functions together and writing higher order functions to support this. I definitely think that using functional languages from time to time has made me a better developer overall and so I encourage people around me, especially at work, to give them a try. Unfortunately that seems to be a steeply uphill battle - people see functional languages as too hard, or think they’re a passing fad, or that there just won’t be enough people to maintain the code because there aren’t enough developers competent in the languages. I just keep hoping that my code is good enough that others will see it, be intrigued, and want to try it themselves. And then, eventually, consider actually using a functional language rather than just using the more functional features of languages like C#.

2 Likes

First, welcome back to Haskell. I’m glad you are interested in giving it another try!

ā€œExperienced developer who bounced off Haskell a couple of timesā€ is one of the core groups of people who I was hoping can benefit from my book. I think Effective Haskell might be a great fit for you!

For projects, I’ve tried to make sure that Effective Haskell has a good mix of shorter and longer projects. A couple of the longer exercises involve building a pager (like the unix cat command), writing a file archive tool (like tar) and building a spellchecking program (like aspell). I think building common command line tools like this can be a great place to start. Programs like this are well understood, so you don’t have to figure out what to build at the same time that you are learning a language and figuring out how to build it. They also have a good mixture of real world edge cases and more theory-focused problems. Another benefit to these types of applications is that they scale well- you can build an extremely simple version, or a much more fully featured application, depending on how much time you have and where your interests are. Finally, it’s really fun to be able to write tools you can actually use every day.

5 Likes

This is a tough situation that I think a lot of FP enthusiasts find themselves in. We are definitely seeing a rising tide of FP in software these days, but there’s still a long way to go.

I’d consider myself a polyglot developer. I love Haskell, but I’ve spent a lot of my career working in all sorts of other languages. I’ve learned a few hard lessons about introducing people to FP, and in particular Haskell, in the workplace.

Perhaps the most important piece of advice I can give is that, in my experience, writing functional with the hope that people will see how elegant it is almost never works. More often than not, people will become resentful and start to more actively reject FP on principle. I’ve definitely seen situations where someone who has been using Haskell for a while brings an idea over to another language, and it caused a rift on the team. In fact, my experience is that people are more likely to reject FP style code from someone they know is an FP enthusiast than they are to reject the same code from someone they don’t think of as a functional programmer. It’s a silly bias, and it won’t be that way everywhere, but I think it’s common enough that you have to address the possibility head on.

The second best piece of advice I can give is, under no circumstances, should you just write some code in Haskell and hope for the best. It’s a great way to have exactly one project in Haskell and to never be allowed to explore other languages in the workplace again. I think it’s a bad knee-jerk reaction teams have, but it’s the common reaction.

Here’s what works best in my own experience: Start by meeting the team where they are, and walk them through the motivation behind the ideas that you want to implement. Don’t present a solution- instead, introduce a series of problems. If you can get a PR in, and add incremental PRs to refactor something into a more functional style, with a clear explanation of what each change brings, and get some reviewers to buy in, that’s a good approach. If you have design or architecture meetings with your team, that’s another approach. Don’t say ā€œlet’s do FPā€- just present a specific real problem in your code and then introduce a very practical fix that happens to borrow from a functional style. Once you’ve done that a few times, and helped people understand the problems you want to solve, and see the patterns for solutions, then you can talk about ways to abstract the ideas into something that borrows ideas from an FP language. Eventually, you might want to say ā€œhey, we’re doing some stuff that is kind of FP style, maybe we should prototype this new feature in Haskell. If we do that, we’ll want to rewrite it in our main language when it goes to production, but it could be instructive.ā€ and then offer to pair with people, write lots of documentation, or whatever else your team likes to do to share information. Maybe even have a reading group (this was another scenario I had in mind for Effective Haskell, and it’s why I spend so much time in the book trying to walk through the motivation behind particular features).

It’s a lot of work, and a lot of it requires paying attention to people’s feelings and the politics of the team as much as the technical motivations, but it can be done. I’ve never turned a team into an entirely Haskell team, but I have had broad team buy in on using Haskell for parts of a system, and had people who didn’t previously have much interest in FP become competent Haskellers able to contribute to that codebase. Not every team will be open to the idea, but a lot will when it’s approach the right way.

5 Likes