-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
shiftMap
is broken
#15
Comments
Thinking about this more, the view model needs an overhaul to be completely safe. I need to be able to specify a view modification function that depends on the type of result returned by the view. I think implementing #14 would also fix the view model, and would be the right way to go about it. |
Am I correct in understanding that while theoretically unsafe the |
Unfortunately the For example, writing a stateful widget like this is safe - counterWidget :: forall a. StateT Int (Widget HTML) a
counterWidget = forever do
count <- get
-- This is safe
-- We use lift so the dom creation methods run in `Widget HTML` monad
lift $ D.div'
[ D.text "Current count is: " <> (show count)
, D.button [P.onClick] [D.text "Increment count"]
]
put (count + 1) However the following is unsafe. This particular example works, but something like this could fail at runtime. I still need to figure out the exact condition that would cause it to fail. counterWidget :: forall a. StateT Int (Widget HTML) a
counterWidget = forever do
count <- get
-- This is unsafe
-- We don't use lift so the dom creation methods run in `StateT Int (Widget HTML)` monad
D.div'
[ D.text "Current count is: " <> (show count)
, D.button [P.onClick] [D.text "Increment count"]
]
put (count + 1) |
All right, after testing a few designs, I settled on - -- | Avoiding monad-control for as long as possible
class ShiftMap s t where
shiftMap :: forall a. (forall b. (a -> b) -> s b -> s b) -> t a -> t a Now we don't have |
Need to think this through in more detail, but I'm still not sure how one could use
Isn't the |
That was my reasoning too when I wrote this. But the result of ‘el’ is a red herring as we have already used ‘unsafeCoerce’ by then. Instead think about the function being passed to ‘shiftMap’. This function only works for a specific ‘a’ (constrained by the type of props passed). However the implementation of ‘shiftMap’ for a specific monad is free to use that function for other ‘a’. For example, the implementation for ‘StateT’ uses it as a ‘m (Tuple s a) -> m (Tuple s a)’. Infact only the implementation for ‘ReaderT’ was actually safe. The lesson here is that no matter how improbable it seems, if the compiler thinks something is unsafe, it will fail at runtime at some point 😀 |
Ah I see, now it makes sense! With your new design, wouldn't the implementation for |
No because it can use the state that was passed in initially. See the implementation for the various transformers in ShiftMap.purs |
This place where I used
shiftMap
is broken - https://github.com/ajnsit/purescript-concur/blob/master/src/Concur/React.purs#L41.That code assumes a
instead of
I overrode the compiler error with
unsafeCoerce
thinking it was safe, but this breaks at runtime whenever thea
gets specialised to something other than justa
.Either the class
ShiftMap
should be changed to haveshiftMap :: forall a. (s a -> s a) -> t a -> t a
, though that might not be implementable for everything (for example I don't think it's possible to write an instance forSignal
s anymore). Or this might be time to reach out forMonadControl
(Not sure if that would solve the issue) or another abstraction (MonadBi
?).With
MonadBi s t
we can write something like the following, which might work -The text was updated successfully, but these errors were encountered: