Based on Data.Typeable cast and Maybe pattern match behavior in Haskell
I have
- a structure:
data S a = S {val :: IO a}
- a structure:
S (Maybe a))
- a structure:
IO (S (Maybe a)))
- a function :
Maybe a -> IO (S (Maybe a))
then, basically need a function
_map :: (Maybe a -> IO (S (Maybe a))) -> S (Maybe a) -> IO (S (Maybe b))
The code is pretty simplified for this question, and there is a reason to have the structures and function like this for my purpose. For instance S {val :: IO a}
actually hold a field value of Data.Vector.Mutable
that is why the type is IO a
.
The basic concept is simply map/function-application on the value.
In the case of Maybe a
is Nothing
, I want the code behaves nothing on IO
and return IO (S (Nothing))
.
data S a = S
{ val :: IO a
}
_val :: S (Maybe a) -> IO (Maybe a)
_val = \sMaybeA -> val sMaybeA
_map :: (Maybe a -> IO (S (Maybe b))) -> S (Maybe a) -> IO (S (Maybe b))
_map = \f -> \sMaybeA -> do
val <- sMaybeA |> _val
sMaybeB <- val >>= f
-- Expected type: IO (S (Maybe a))
-- Actual type: Maybe (S (Maybe a))
pure sMaybeB
-- another obvious error because sMaybeB has the error
Of course, the error is obvious so I understand how this occurs, and that is not the scope of this quesion.
I simply can't figure out how to accomplish the expected type with the mixed structure of IO
and Maybe
. How can you fix this out?
CodePudding user response:
I'm not sure about what you are trying to achieve. The way you nest IO
within another IO
(aka S
) is a bit strange.
Also, your type for _map
only admits trivial inhabitants:
_map :: (Maybe a -> IO (S (Maybe a))) -> S (Maybe a) -> IO (S (Maybe b))
Note the b
at the very end. There is no way to obtain such a b
from the arguments, forcing the code to always use Nothing :: Maybe b
, suitably wrapped. I guess this is not what you want to do.
I changed the b
into an a
. Then we can test the result of sMaybeA
and act accordingly.
_map :: (Maybe a -> IO (S (Maybe a))) -> S (Maybe a) -> IO (S (Maybe a))
_map f sMaybeA = do
v <- val sMaybeA
case v of
Nothing -> return (S (return Nothing))
Just _ -> f v
Note that, while this compiles, it could be something different from what you actually need.
CodePudding user response:
I feel like your concerns about Haskell's type system stem from the misleading API design you've showcased here. You're trying to nest your types rather than compose them. I believe that, while writing Haskell, you'll find that Haskell types compose very well with each other. You don't have to fallback to naive yet ugly nesting.
What about something like this instead?
newtype S a = S { val :: IO (Maybe a) }
instance Functor S where
fmap f = S . fmap (fmap f) . val
instance Applicative S where
pure = S . pure . pure
S f <*> S v = S $ liftA2 (<*>) f v
instance Monad S where
S ma >>= f = S $ do
maybex <- ma
case maybex of
Nothing -> pure Nothing
Just a -> val $ f a
weirdMap :: (a -> S b) -> S a -> S b
weirdMap f sa = do
value <- sa
f value
There's no nested IO
, because you never need nested IO
(unless you're working with STM
). You can simply combine your IO
s, doesn't matter if one of them is just for a mutable array.
I've also taken the liberty to embed the Maybe a
within S
, as it seems like that's what your API does anyway. However, you may very well take it out and still proceed with the Functor
/Applicative
/Monad
instances, and continue to keep a flat IO
hierarchy.