Consider this type:
data Vec3 = Vec3 {_x, _y, _z :: Int}
I have some functions that all take the same input, and may fail to compute a field:
data Input
getX, getY, getZ :: Input -> Maybe Int
I can write a function that tries all three of these field constructors:
v3 :: Input -> Maybe Vec3
v3 i = liftA3 Vec3 (getX i) (getY i) (getZ y)
But it's kinda annoying to have to pass that i
input around three times. Functions are themselves an applicative functor, and so one can replace foo x = bar (f x) (g x) (h x)
with foo = liftA3 bar f g h
.
Here, my bar
is liftA3 Vec3
, so I could write
v3' :: Input -> Maybe Vec3
v3' = liftA3 (liftA3 Vec3) getX getY getZ
But this is a bit gross, and when we work with composed applicatives in this way (((->) Input)
and Maybe
), there's the Compose
newtype to handle this kind of thing. With it, I can write
v3'' :: Input -> Maybe Vec3
v3'' = getCompose go
where go = liftA3 Vec3 x y z
x = Compose getX
y = Compose getY
z = Compose getZ
Okay, not exactly a great character savings, but we're now working with one combined functor instead of two, which is nice. And I thought I could use coerce
to win me back some of the characters: after all, x
is just a newtype wrapper around getX
, and likewise for the other fields. So I thought I could coerce liftA3
into accepting three Input -> Maybe Vec3
instead of accepting three Compose ((->) Input) Maybe Vec3
:
v3''' :: Input -> Maybe Vec3
v3''' = getCompose go
where go = coerce liftA3 Vec3 getX getY getZ
But this doesn't work, yielding the error message:
tmp.hs:23:14: error:
• Couldn't match representation of type ‘f0 c0’
with that of ‘Input -> Maybe Int’
arising from a use of ‘coerce’
• In the expression: coerce liftA3 Vec3 getX getY getZ
In an equation for ‘go’: go = coerce liftA3 Vec3 getX getY getZ
In an equation for ‘v3'''’:
v3'''
= getCompose go
where
go = coerce liftA3 Vec3 getX getY getZ
|
23 | where go = coerce liftA3 Vec3 getX getY getZ
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I don't understand why not. I can write coerce getX :: Compose ((->) Input) Maybe Int
, and this is fine. And generally a function can be coerced in order to make it coerce its arguments or return type, as in
coerce ((+) :: Int -> Int -> Int) (Max (5::Int)) (Min (8::Int)) :: Sum Int
And I can in fact write out all the coerce
s individually:
v3'''' :: Input -> Maybe Vec3
v3'''' = getCompose go
where go = liftA3 Vec3 x y z
x = coerce getX
y = coerce getY
z = coerce getZ
So why can't liftA3
itself be coerced to accept getX
instead of coerce getX
, allowing me to use v3'''
?
Since a composable function can be executed multiple times or can skip execution altogether, mutating or relying on global state in composable functions can lead to unforeseen errors or bugs. Hence relying on side-effects is not the recommended approach.
Compose enables you to create beautiful apps with direct access to the Android platform APIs and built-in support for Material Design, Dark theme, animations, and more: “Compose has also solved more than declarative UI -- accessibility apis, layout, all kinds of stuff have been improved.
The @Preview annotation lets you preview your composable functions within Android Studio without having to build and install the app to an Android device or emulator. The annotation must be used on a composable function that does not take in parameters.
If you provide the applicative functor to liftA3
, then the following typechecks:
v3' :: Input -> Maybe Vec3
v3' = coerce (liftA3 @(Compose ((->) Input) Maybe) Vec3) getX getY getZ
In coerce liftA3
without any annotation, there is no way to infer what applicative functor to use liftA3
with. Neither of these even mention the type Compose
. It might just as well be ReaderT Input Maybe
, Kleisli Maybe Input
, another type with an unlawful instance or something even more exotic.
In getCompose (coerce liftA3 _ _ _)
(your last attempt), the getCompose
does not constraint liftA3
("inside" of coerce
), because getCompose
is "outside" of coerce
. It requires that the result type of liftA3
is coercible to Compose ((->) Input) Maybe Vec3
, but it might still not be equal to that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With