Hello,
I’d like to ask whether this should be possible or not.
The code:
viewMenuItemLink : ({ a | id : Int } -> Bool) -> { a | id : Int, title : String } -> Html Msg
viewMenuItemLink isActive ({ id, title } as item) =
Components.menuListItemLink (isActive item)
[ Events.onClick (SelectMenuItem id) ]
[ text title ]
I tried even to change one of narrow annotation’s type variable so that they differ: viewMenuItemLink : ({ a | id : Int } -> Bool) -> { b | id : Int, title : String } -> Html Msg
This was pretty puzzling to me too, but I figured it out!
When you define a function like:
test : { a | foo : Int } -> Bool
test a = test.foo == 5
Then { a | foo : Int } means that it works with any record with and foo : Int field, as you expect.
But when you define a function like:
test : ({ a | foo : Int } -> Bool) -> { foo : Int } -> Bool
test fn a =
fn a
What that says is not that the passed in function has to work with any record with a foo : Int field, but that the passed in function decides what fields it requires, beyond the foo : Int. So it would for example be valid to pass a { foo : Int, bar : Int } -> Bool function, and that of course wouldn’t work with the { foo : Int } record!
Thanks for spending time on that.
I dont quite undestand this part:
What do you mean by “…what fields it requires, beyond the foo : Int.”. Doesnt that test function accept as 1st argument a function that accepts map that has field foo : Int and does not care about other fields?
I tried:
As last getField1 invocation suggests, it does not need any more fields “beyond” field1 field.
But tying to fit “wide narrow type” to function that accepts “more narrow type” just does not work. (compiler error)
useShortAndLongType : ({ a | field1 : Int } -> Bool) -> { a | field1 : Int, field2 : Bool } -> Bool
useShortAndLongType getField1Fn longerType =
getField1Fn longerType
I think that it would be easier to just invoke passed-in fn like this:
But looking at this now… If I would need to pass to getField1Fn more fields, another extracting function could make sense… Although just simply passing to getField1Fn more params could also be an option that in addition does not require any intermediate functions (but that would depend on frequency of using such getField1Fn function not to break DRY principle).
Ohhh, I might just understood what you are saying here… That the annotation: ({ a | foo : Int } -> Bool) says that it is a function that accepts one param which must contain foo : Int field. Wow, would not even try to look on that from the other way around…
But than such annotation would not make sense, would it?
I’ve been using Elm for 1.5 years and I got to learn something new too
Here’s a simpler example of the same thing.
If you define a List to be of type a, you’re saying that it can hold any type, which would be impossible, except for the empty list, for which it is “vacuously true”.
empty : List a
empty = []
But when you’re saying that a function takes List a, it’s the opposite - all lists are valid arguments.
length: List a -> Int
lenght = ...
So in the first case, you’re placing a constraint on the list, and in the second case you’re unconstraining the list.
I see. So with that function annotation you really say that any function is valid when it accepts first argument as a map containing given fields e.g. the mentioned foo : Int