Msg organisation and currying with custom types


#1

I have this demo code, where I have a record in my model, which represents my domain object, which again contains a list of records of a related domain object (Template, which contains a list of Group). I’ve tried to create my messages for updating these records from these design goals:

  • I want my messages to clearly signal when they make sense to send and receive (my current design does this by nesting messages, since you can’t update a Group in the UI without updating the Template the group resides in)
  • The user can add an arbitrary number of Groups to a template, so I need messages to specify the exact group that is being updated (which I’ve done by putting the index of the group in the message)

I have two questions for this:
1: Am I going about this in a sensible way, or is there a more idiomatic way to do it?
2: This way of organizing Msgs would work quite nicely with currying, but it seems like I’m missing a way to do currying with types only; my workaround is putting it in a function ala:

viewGroupEditor : Int -> Group -> Html Msg
viewGroupEditor index group =
    div [] [
                input [placeholder "Group Name", value group.name, onInput (groupNameMsg index) ] []
            ]


groupNameMsg : Int -> String -> Msg
groupNameMsg index input =
    UpdateTemplate (EditGroup index (UpdateGroupName input))

Is there a way to do currying just with types?


#2

I think there is some difficulty in the terminology you’re using.
Currying is the process of turning a multiple argument function in to nested single argument functions. This is something the Elm compiler does for all functions. All functions in Elm are curried.

You can apply an argument to a function and get back another function, the function you get back is a partially applied version of the original function. Having curried functions by default in the language makes partial application rather nice and easy. But you can do partial application of functions in some languages that don’t have curried functions.

In your code example you can compose the constructors for the various parts of your Msg type by using the function composition operators >> or <<
https://package.elm-lang.org/packages/elm/core/latest/Basics#>>

eg.

groupNameMsg : Int -> String -> Msg
groupNameMsg index input =
    UpdateGroupName >>  EditGroup index >> UpdateTemplate

You need to apply the index argument to EditGroup as it doesn’t come from the output of UpdateGroupName.

Deeply nesting values like this can be a bit of a pain, as you have to extract them again to use them. But whether this is good or a pain really depends on the use case.


#3

Ah yes, I’m conflating different concepts, thanks! I was not aware the two were not synonymous :slight_smile:

And the composition operator does indeed do what I want, and allows me to inline my nested messages (with operators reversed from your example); I might still pull them out for readability, but now I’m not forced to at least :blush:

viewGroupEditor : Int -> Group -> Html Msg
viewGroupEditor index group =
    div [] [
                input [placeholder "Group Name", value group.name, onInput (UpdateTemplate << EditGroup index << UpdateGroupName) ] []
           ]

(also in Ellie)

And I don’t mind nesting in general, though it depends on the problem, as you said. I’d still love to see examples of how others might choose to organize their messages in a case such as this :slight_smile:


#4

For the purposes of your question and really in the context of applied computer science the differences between currying and partial application are not all that important. Exactly defining that distinction is not holding you back nor does it answer your question.

Currying:

becomes

Partial application:

after “fixing” X becomes

Elm has currying, that gets translated to partial application in JS. But who cares. :slight_smile:

What is important to understand, and what you are conflating, is the difference between a Type, and a Type Constructor

A Type is a specification for a set of values that can be passed around your application.

A Type Constructor is a function that creates a value of a type.

For example in this type definition:

type Something a b
 = Thing a b

The Type is Something a b (which at compile time gets resolved to a concrete type, like Something String Int)

Thing a b is a Type Constructor which creates a value of the type Something a b. :slight_smile:

Ok getting to the meat here,
Since Type Constructors are functions, they are curried like any other function and in fact, because we are in elm, we always think of function application as currying. Ok, and because sometimes we don’t want to have to write a wrapper function to handle the currying, we can use the compose operators << and >> to do it for us.

Consider the following:

myTodo = Debug.todo << Debug.toString 

myTodo x =
  Debug.todo (Debug.toString x)

In that contrived example, both implementations of myTodo are equivalent. Notice that in the top definition, the final argument is not specified as function composition implies we need another argument. In contrast, in the 2nd example we must specify the argument as we are using regular function application / currying. The 2nd style is preferred in elm as it is typically easier to read especially for newcomers.

Which all brings us to:

Is there a way to do currying just with types?

No, because Types are not functions, but Type Constructors can be curried like any other function.

[edit]
Seems like the function images from wikipedia show up just fine in the edit preview, but not so much in the actual post. Not sure this is a bug anyone wants to fix or a non feature but there’s that.


#5

My lack of distinction between types and type constructors was mainly due to laziness. I never had the thought that constructors aren’t special, but merely functions in disguise though; thanks for lesson :blush:


closed #6

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.