While working on some logic on my current project, I came across a weird situation, where I have two functions with the same signature, one existing outside the context of a third function (“filterOutside”), and the second within that same context (“filterWithParam”).
The issue is as follows. When using the first example, using the outside function in the filter, I can correctly use it on both TextMaps as the the signature of the “filterForKei” function remains generic. In the second example, using the param function in the filter, I cannot use the “filterForKey” function in both TextMaps, as the function signature infers the first TextMap.
I would like to know what is causing this, as it is a bit confusing.
Thank you!
filterOutside : Text -> Bool
filterOutside _ = True
testFunc : (Text -> Bool) -> Text
testFunc filterWithParam = ""
where
textMap1 : TextMap Int = fromList [("Test", 1)]
textMap2 : TextMap (Int, Int) = fromList [("Test", (1, 2))]
-- Filter using outside function
filterForKei = filterWithKey (const . filterOutside)
filtered1 = filterForKei textMap1
filtered2 = filterForKei textMap2
-- Filter using param function
filterForKey = filterWithKey (const . filterWithParam)
filtered3 = filterForKey textMap1
filtered4 = filterForKey textMap2
This is due to a Haskell extension called MonoLocalBinds which is turned on by default in Daml.
By default, Haskell generalizes bindings that look like function (the fact that it doesn’t generalize non-functions is what is called the MonomorphismRestriction).
MonoLocalBinds restricts this a bit further. You can read the details in the linked documentation but let me summarize it for your example:
filterForKei has only top-level definitions as free variables which are closed so it is generalized.
filterForkey has filterWithParam as a free variable which is not closed so it is not generalized.
This is perhaps somewhat unintuitive. I’d generally recommend that whenever you run into confusing type inference issues, you add some more type signatures which are often sufficient to resolve the issue, e.g., if you add the type signature
Actually in my testing I did have the signature as you described, but it didn’t solve the issue for me, so I didn’t think to included in the thread. But I’ll have a read and find some other way to resolve the issue.
Thank you!
Nevermind, in your compiling example you changed the “testFunc” signature, and I thought that was the reason why it was working. But now I see that you added the function signature to the line above.
The reason I said it didn’t work is beacuse I tried to do the same, but in an inline fashion, like so
filterForkey : TextMap a -> TextMap a = filterWithKey (const . filterWithParam)
and it that doesn’t compile.
Regardless, thanks for your help.
I sometimes try that too, out of habit from other languages (Scala I guess?), but that is not actually valid Daml syntax: type signatures have to go on separate lines.