adius [6:27 AM]
How can I implement such a function: foreign import doSomething :: String -> Maybe Number ?
justinw [6:28 AM]
you have to give the constructors to the JS also (a -> Maybe a) and (Maybe a), Just and Nothing
otherwise you can choose to return foreign and just return either the read result or Nothing on both Nothing and a parse failure
foreign import doSomethingImp :: forall a. (a -> Maybe a) -> Maybe a -> String -> Maybe Number
doSomething :: String -> Maybe Number
doSomething = doSomethingImpl Just Nothing
You could also replace a with Number if you wanted.
But toMaybe essentially does ^
foreign import doSomethingImp :: (forall a. a -> Maybe a) -> (forall a. Maybe a) -> String -> Maybe Number
(moving the foralls into the constructors)
As it reduces the number of possible implementations for the passed-in Just/Nothing such that they’re basically the only allowable arguments.
There used to be a section in the FFI docs about this technique, but I guess it was deleted at some point after transitioning off the wiki?
How does that work? I would have thought that the first argument could be Justorconst Nothing and the second argument would have to be Nothing in either case.
foreign import lol :: forall a. (a -> Maybe a) -> Maybe a -> Something
which can be called like
lol (Just <<< (_ + 1)) (Just 0)
By putting the forall right at the beginning, you’re saying the caller is allowed to pick any type a, and the implementation has to be able to deal with that (no matter what they pick). If you put it inside the individual arguments it’s the other way around: you’re saying that the implementation is allowed to pick any type a to use those arguments with, and whatever the caller supplies has to work with any possible choice of a.
doSomethingA
:: (forall a. a -> Maybe a)
-> (forall a. Maybe a)
-> String
-> Maybe Number
is not the same as
doSomethingB
:: forall a b
. (a -> Maybe a)
-> Maybe b
-> String
-> Maybe Number
because the former puts an obligation on the caller to ensure that the first two arguments work with any type a at all, whereas the latter allows the caller to choose what a and b are. For example, I can write
doSomethingB (\x -> Just (x + 1)) (Just "lol")
(which is of course not good if we are expecting Just and Nothing). But trying to call doSomethingA with those arguments won’t work. In fact, there are only two ways of constructing a forall a. a -> Maybe a without resorting to type system escape hatches: Just, and const Nothing. Likewise there’s only one way of constructing a forall a. Maybe a, and that’s Nothing.
I’m curious why the constructors for Just and Nothing couldn’t be imported within the the foreign.js itself instead of having to pass them in as params to doSomething?
They could be, but I wouldn’t recommend that as an approach because it requires you to make more assumptions than providing them on the PS side. It also means the compiler can’t check your work.