In Elm, you are discouraged to use function values in either the Model or Msg parts of the Elm Architecture.
As far as I can tell, there are two main reasons for this:
-
The model should be a description of the application state, not the processes that manipulate it. Similarly, the messages should describe the changes to an application, not implement them.
-
A more technical reason is that in Elm we cannot equate functions (this causes a runtime error, ouch!) nor can we serialize them (the serialization libraries provide no means to do this). This means that some nice Elm features like the time travelling debugger or model export, etc. are not available.
It is important to distinguish these two reasons and not conflate them. The first is architectural and is about what is a good idea to make maintainable and reliable applications. As such it is opinionated, but also independent of implementation.
The second is a particular implementation detail of Elm. There are languages which allow to equate functions (Unison) or serialize them (i.e. JavaScript). This argument only holds if we keep the current implementation of Elm as a constant, not universally.
The problem with these properties is that they break encapsulation. If I expose you a datastucture from my module, you should not need to care how it's implemented. You should be able to stick it in your model (or message) and call it a day. However, if I as module author sneakily use functions to implement the datastructure (for which there are legitimate reasons), you may be breaking the above guidelines without even knowing. As such I think that reason #1 really mostly applies for directly using functions, not functions that are parts of some other implementation. But for that to be pratical, we need some solutions for reason #2.
Sure.
module A exposing (Foo, bar, baz, foo)
type Foo
= Bar Int
| Baz (() -> Int)
bar : Int -> Foo
bar = Bar
baz : Int -> Foo
baz n =
Baz (\() -> n)
foo : Foo -> Int
foo f =
case f of
Bar n ->
n
Baz fn ->
fn ()
module A exposing (myFun)
import Foo exposing (Foo)
buzzing : Foo -> Foo -> Bool
buzzing a b =
a == b
module ATest exposing (testBuzzing)
-- imports
testBuzzing =
test "buzzing is true for same values" <|
\() ->
Foo.bar 2
|> A.buzzing (Foo.bar 2)
|> Expect.equal True
Tests pass, runtime error in production. Ouch.