Lessons learnt by Mikael Tönnberg

April 19, 2023

In the Lessons Learnt presentation by Mikael Tönnberg, he shares his experiences working with the technologies used for Carbon Clouds’ climate intelligence platform, including Haskell, Elm, and AWS. Mikael, the co-founder and CTO of Carbon Cloud, expresses satisfaction with their technology choices, which have resulted in few bugs and stable applications. He highlights the benefits of using functional programming languages like Haskell and Elm, such as ease of refactoring and good performance. However, he acknowledges the challenges, like fewer maintained packages for niche applications and integrations in Elm and the learning curve of handling complex concepts in Haskell. Mikael also discusses the importance of using strongly typed languages and the concepts of cardinality and arity. He emphasizes the benefits of having both property-based tests and example-based tests and the importance of balancing the cost of expressing properties in types versus using tests. Mikael made the decision to use Haskell and Elm for Carbon Cloud based on their competence for the back end and front end, respectively, and the importance of a strong engineering department as a business enabler.

Lessons learnt: A comprehensive overview

Embracing Functional Programming

Choosing Haskell and Elm

Mikael Tönnberg emphasizes the significant benefits of using Haskell and Elm in their projects. These functional programming languages have allowed Carbon Cloud to maintain stable applications with minimal bugs. Haskell’s strong typing and lazy evaluation, along with Elm’s straightforward approach to front-end development, have resulted in ease of refactoring and impressive out-of-the-box performance.

 

The advantages and challenges of Elm

Elm’s simplicity and ability to maintain solutions to complex problems make it a standout choice for web development. However, Mikael notes that Elm lacks a broad range of maintained packages for niche applications and integrations compared to more mainstream languages. Additionally, Elm’s handling of updates and messages in the UI might not be ideal for animation-heavy applications.

 

Overcoming Haskell’s challenges

Haskell’s lazy evaluation and lack of comprehensive documentation initially posed challenges, especially with race conditions and global state management. Despite these hurdles, Mikael found that the high competence level among applicants mitigated potential difficulties. The strong type system in Haskell ensures reliability and correctness, crucial for commercial applications.

 

The importance of strongly typed languages

Mikael introduces the concepts of cardinality and arity to highlight the importance of strongly typed languages. Cardinality refers to the number of unique values a function’s input can have, while arity refers to the number of arguments a function takes. Strongly typed languages help ensure correct functionality and efficient use of resources, especially when dealing with high-cardinality inputs that require extensive testing.

 

Complexity and testing

Function behavior and errority

Mikael discusses the complexity of function behavior and how errority (errors) impacts the total kernel, which is the calculation of a function’s possible outputs. He explains that the more inputs a function has, the more permutations can yield different results, increasing complexity. Refactoring types to simplify data structures without changing the kernelity can reduce the effort required for testing.

 

Example-based and property-based testing

Example-based tests provide specific, concrete inputs and expected outputs, ensuring desired behavior in specific instances. However, as system complexity increases, the coverage provided by these tests may be insufficient. Mikael highlights property-based testing as an alternative, where properties describe expected behavior, and example-based tests are generated based on these properties. This approach can reduce the number of tests needed but requires understanding its limitations and the importance of both test types.

 

Practical implementation insights

Evaluating and removing unnecessary code

Mikael underscores the importance of focusing on the algorithm or pipeline that solves the problem. Changing data structures, like using a dictionary instead of a list, can simplify code and reduce kernelity. He also mentions the GDP library in Haskell as a tool to enhance type systems in less capable languages, though implementing stronger types can come with costs, including runtime costs and maintenance overhead.

 

Balancing costs and benefits of strong typing

Mikael discusses the balance between the cost of expressing properties in types and using example-based tests. While strong types reduce the need for tests, their implementation can be expensive. Following a balanced approach, Mikael suggests adding types when the cost is low and using property-based tests for more complex properties.

 

Event sourcing and functional programming

Mikael shares his experience with event sourcing, which fits well with their front-end architecture. By conceptually looping between view, state, and generating messages to update the state, event sourcing aligns with their immutable data paradigm. This approach has proven effective in maintaining consistency and reliability in their system.

 

Conclusion

Mikael Tönnberg’s journey with functional programming languages Haskell and Elm offers valuable insights into building robust and reliable applications. Emphasizing the importance of strong typing, balancing testing approaches, and leveraging event sourcing, Mikael provides a comprehensive guide for developers looking to explore functional programming. Despite the challenges, the benefits of these technologies in maintaining stable and performant applications are evident, making them a worthy consideration for modern software development.

 

Additional resources

Check out more from the MeetUp Func Prog Sweden. Func Prog Sweden is the community for anyone interested in functional programming. At the MeetUps the community explore different functional languages like Erlang, Elixir, Haskell, Scala, Clojure, OCaml, F# and more.