4 ms·
The code is mostly pure Haskell (the do block, which most Haskellers are familiar with), with a few domain-specific elements (those led and delay “assignments”
by one-punch 7y ago
The code is mostly pure Haskell (the do block, which most Haskellers are familiar with), with a few domain-specific elements (those led and delay “assignments” and the Stream type in this functional reactive programming framework arduino-copilot, whose meaning is specific to this Arduino application and most Haskellers have to guess).
The short answer is that I could guess what it means.
This `arduino-copilot` example really showcases one killer application of Haskell: writing eDSL (embedded domain-specific language) in Haskell.
Haskell allows really high level abstraction: in particular, you can overload the operator which combines different statements in Haskell. In C/C++, the semicolon combines subsequent statements, Haskell even allows you to overload (give a new meaning to) the semicolon “operator” (think of overloading ; instead of overloading +). That is, in Haskell we can interpret differently how to combine subsequent statements in different contexts (such as null-checking, exception handling, async/await style concurrency, looping, or procedural state passing, etc., see the example below).
However, when we “overload the semicolon operator”, we intuitively have some expectations:
1. We expect to be able to turn a value into a standalone statement (to inject a value as a single statement with that value, e.g., to turn the value “97” into the statement “97;”, and this operation is called return)
2. We expect to be able to use the result of a statement in the next statement as in
str <- getLine;
putStrLn str
This could be rewritten using a bind operation (denoted >>=) as getLine >>= putStrLn.
When we package up (encapsulate) the above two operations (return and bind), we expect the two operations to play nicely with each other to satisfy some intuitive laws (e.g,. if you inject a value then use the value in a subsequent functional statement, it is the same as just calling the function on the value, so the return and bind cancels out, etc.). A mathematical formalization of this idea (the two operations with some intuitive laws) is named a monad, which happens to generalize many different computational contexts of combining statements such as null-checking, exception handling, async/await style concurrency, looping, or procedural state passing [1].
[1]: https://philipnilsson.github.io/Badness10k/posts/2017-05-07-escaping-hell-with-monads.html https://philipnilsson.github.io/Badness10k/posts/2017-05-07-...
The ability to reinterpret statement compositions is why Haskell is so great for embedding domain-specific languages (even procedural eDSL), as the arduino article shows, and this is why some people call Haskell the finest procedural language.