Struggling with implementing a Monad function
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
Recently, I'm playing around with Haskell monad and trying to learn about this concept.
Let's say there is a tree data type declared which can have multiple sub-trees.
data MyTree a = MyTree a [MyTree a]
And I'm trying to implement a function that returns "Nothing" if the tree contains any "Nothing" value in the tree. Else, extract all m value and returns a wrapped tree.
So the function type signature has the following.
check :: Monad m => MyTree (m a) -> m (MyTree a)
And here is my current implementation.
check (MyTree v ) = v >>= (v' -> return (MyTree v' ))
check (MyTree v (x:xs)) =
v >>= (v' -> check x >>= (t' -> return (MyTree v' [t'])))
I use a bind operator on v so that I can get a pure value of it. Then I call the "check" function recursively with the head value from the list. Finally, I wrap the final results.
I tested with some samples and got the following results.
> test1 = MyTree (Just 1) [MyTree (Just 2) [MyTree (Just 3) ]]
> check test1
Just (MyTree 1 [MyTree 2 [MyTree 3 ]])
> test2 = MyTree (Just 1) [MyTree (Just 2) , MyTree (Just 3) ]
> check test2
-- expected: Just (MyTree 1 [MyTree 2 , MyTree 3 ]
-- actual: Just (MyTree 1 [MyTree 2 ])
So, the current implementation has a problem when the input tree has multiple sub-trees. And I have realized that the problem is that I'm only using x
but not xs
. I wrapped my head around to think of the right approach and still figuring out. It will be very helpful if anyone has an idea for this.
haskell functional-programming monads
add a comment |
Recently, I'm playing around with Haskell monad and trying to learn about this concept.
Let's say there is a tree data type declared which can have multiple sub-trees.
data MyTree a = MyTree a [MyTree a]
And I'm trying to implement a function that returns "Nothing" if the tree contains any "Nothing" value in the tree. Else, extract all m value and returns a wrapped tree.
So the function type signature has the following.
check :: Monad m => MyTree (m a) -> m (MyTree a)
And here is my current implementation.
check (MyTree v ) = v >>= (v' -> return (MyTree v' ))
check (MyTree v (x:xs)) =
v >>= (v' -> check x >>= (t' -> return (MyTree v' [t'])))
I use a bind operator on v so that I can get a pure value of it. Then I call the "check" function recursively with the head value from the list. Finally, I wrap the final results.
I tested with some samples and got the following results.
> test1 = MyTree (Just 1) [MyTree (Just 2) [MyTree (Just 3) ]]
> check test1
Just (MyTree 1 [MyTree 2 [MyTree 3 ]])
> test2 = MyTree (Just 1) [MyTree (Just 2) , MyTree (Just 3) ]
> check test2
-- expected: Just (MyTree 1 [MyTree 2 , MyTree 3 ]
-- actual: Just (MyTree 1 [MyTree 2 ])
So, the current implementation has a problem when the input tree has multiple sub-trees. And I have realized that the problem is that I'm only using x
but not xs
. I wrapped my head around to think of the right approach and still figuring out. It will be very helpful if anyone has an idea for this.
haskell functional-programming monads
3
As a hint, try writing an auxiliary function[m (MyTree a)] -> m [MyTree a]
first (or usesequence
from the libraries). Then think about how you can exploit that forxs
. You will probably realize that you don't even need to handle the first elementx
in a special way, but you can directly usecheck (MyTree v xs) =...
.
– chi
Nov 21 '18 at 23:33
Thanks @chi. One more question!xs
has a type[MyTree (m a)]
and how do you feed that in an auxiliary function,[m (MyTree a)] -> m [MyTree a]
? I'm getting it to the right way but bridging between these two is quite confusing.
– ykdojo
Nov 22 '18 at 2:07
That's correct, we need to turn[MyTree (m a)]
into[m (MyTree a)]
. We could usemap f
for that, for somef :: MyTree (m a) -> m (MyTree a)
. Do we have such a function?
– chi
Nov 22 '18 at 9:15
By "Nothing", do you mean the valueNothing :: Maybe a
? Your function type should beMyTree (Maybe a) -> Maybe (MyTree a)
; no general constraintMonad m =>
is needed.
– chepner
Nov 22 '18 at 15:35
add a comment |
Recently, I'm playing around with Haskell monad and trying to learn about this concept.
Let's say there is a tree data type declared which can have multiple sub-trees.
data MyTree a = MyTree a [MyTree a]
And I'm trying to implement a function that returns "Nothing" if the tree contains any "Nothing" value in the tree. Else, extract all m value and returns a wrapped tree.
So the function type signature has the following.
check :: Monad m => MyTree (m a) -> m (MyTree a)
And here is my current implementation.
check (MyTree v ) = v >>= (v' -> return (MyTree v' ))
check (MyTree v (x:xs)) =
v >>= (v' -> check x >>= (t' -> return (MyTree v' [t'])))
I use a bind operator on v so that I can get a pure value of it. Then I call the "check" function recursively with the head value from the list. Finally, I wrap the final results.
I tested with some samples and got the following results.
> test1 = MyTree (Just 1) [MyTree (Just 2) [MyTree (Just 3) ]]
> check test1
Just (MyTree 1 [MyTree 2 [MyTree 3 ]])
> test2 = MyTree (Just 1) [MyTree (Just 2) , MyTree (Just 3) ]
> check test2
-- expected: Just (MyTree 1 [MyTree 2 , MyTree 3 ]
-- actual: Just (MyTree 1 [MyTree 2 ])
So, the current implementation has a problem when the input tree has multiple sub-trees. And I have realized that the problem is that I'm only using x
but not xs
. I wrapped my head around to think of the right approach and still figuring out. It will be very helpful if anyone has an idea for this.
haskell functional-programming monads
Recently, I'm playing around with Haskell monad and trying to learn about this concept.
Let's say there is a tree data type declared which can have multiple sub-trees.
data MyTree a = MyTree a [MyTree a]
And I'm trying to implement a function that returns "Nothing" if the tree contains any "Nothing" value in the tree. Else, extract all m value and returns a wrapped tree.
So the function type signature has the following.
check :: Monad m => MyTree (m a) -> m (MyTree a)
And here is my current implementation.
check (MyTree v ) = v >>= (v' -> return (MyTree v' ))
check (MyTree v (x:xs)) =
v >>= (v' -> check x >>= (t' -> return (MyTree v' [t'])))
I use a bind operator on v so that I can get a pure value of it. Then I call the "check" function recursively with the head value from the list. Finally, I wrap the final results.
I tested with some samples and got the following results.
> test1 = MyTree (Just 1) [MyTree (Just 2) [MyTree (Just 3) ]]
> check test1
Just (MyTree 1 [MyTree 2 [MyTree 3 ]])
> test2 = MyTree (Just 1) [MyTree (Just 2) , MyTree (Just 3) ]
> check test2
-- expected: Just (MyTree 1 [MyTree 2 , MyTree 3 ]
-- actual: Just (MyTree 1 [MyTree 2 ])
So, the current implementation has a problem when the input tree has multiple sub-trees. And I have realized that the problem is that I'm only using x
but not xs
. I wrapped my head around to think of the right approach and still figuring out. It will be very helpful if anyone has an idea for this.
haskell functional-programming monads
haskell functional-programming monads
edited Nov 22 '18 at 1:26
dfeuer
33.7k349133
33.7k349133
asked Nov 21 '18 at 23:23
ykdojoykdojo
254
254
3
As a hint, try writing an auxiliary function[m (MyTree a)] -> m [MyTree a]
first (or usesequence
from the libraries). Then think about how you can exploit that forxs
. You will probably realize that you don't even need to handle the first elementx
in a special way, but you can directly usecheck (MyTree v xs) =...
.
– chi
Nov 21 '18 at 23:33
Thanks @chi. One more question!xs
has a type[MyTree (m a)]
and how do you feed that in an auxiliary function,[m (MyTree a)] -> m [MyTree a]
? I'm getting it to the right way but bridging between these two is quite confusing.
– ykdojo
Nov 22 '18 at 2:07
That's correct, we need to turn[MyTree (m a)]
into[m (MyTree a)]
. We could usemap f
for that, for somef :: MyTree (m a) -> m (MyTree a)
. Do we have such a function?
– chi
Nov 22 '18 at 9:15
By "Nothing", do you mean the valueNothing :: Maybe a
? Your function type should beMyTree (Maybe a) -> Maybe (MyTree a)
; no general constraintMonad m =>
is needed.
– chepner
Nov 22 '18 at 15:35
add a comment |
3
As a hint, try writing an auxiliary function[m (MyTree a)] -> m [MyTree a]
first (or usesequence
from the libraries). Then think about how you can exploit that forxs
. You will probably realize that you don't even need to handle the first elementx
in a special way, but you can directly usecheck (MyTree v xs) =...
.
– chi
Nov 21 '18 at 23:33
Thanks @chi. One more question!xs
has a type[MyTree (m a)]
and how do you feed that in an auxiliary function,[m (MyTree a)] -> m [MyTree a]
? I'm getting it to the right way but bridging between these two is quite confusing.
– ykdojo
Nov 22 '18 at 2:07
That's correct, we need to turn[MyTree (m a)]
into[m (MyTree a)]
. We could usemap f
for that, for somef :: MyTree (m a) -> m (MyTree a)
. Do we have such a function?
– chi
Nov 22 '18 at 9:15
By "Nothing", do you mean the valueNothing :: Maybe a
? Your function type should beMyTree (Maybe a) -> Maybe (MyTree a)
; no general constraintMonad m =>
is needed.
– chepner
Nov 22 '18 at 15:35
3
3
As a hint, try writing an auxiliary function
[m (MyTree a)] -> m [MyTree a]
first (or use sequence
from the libraries). Then think about how you can exploit that for xs
. You will probably realize that you don't even need to handle the first element x
in a special way, but you can directly use check (MyTree v xs) =...
.– chi
Nov 21 '18 at 23:33
As a hint, try writing an auxiliary function
[m (MyTree a)] -> m [MyTree a]
first (or use sequence
from the libraries). Then think about how you can exploit that for xs
. You will probably realize that you don't even need to handle the first element x
in a special way, but you can directly use check (MyTree v xs) =...
.– chi
Nov 21 '18 at 23:33
Thanks @chi. One more question!
xs
has a type [MyTree (m a)]
and how do you feed that in an auxiliary function, [m (MyTree a)] -> m [MyTree a]
? I'm getting it to the right way but bridging between these two is quite confusing.– ykdojo
Nov 22 '18 at 2:07
Thanks @chi. One more question!
xs
has a type [MyTree (m a)]
and how do you feed that in an auxiliary function, [m (MyTree a)] -> m [MyTree a]
? I'm getting it to the right way but bridging between these two is quite confusing.– ykdojo
Nov 22 '18 at 2:07
That's correct, we need to turn
[MyTree (m a)]
into [m (MyTree a)]
. We could use map f
for that, for some f :: MyTree (m a) -> m (MyTree a)
. Do we have such a function?– chi
Nov 22 '18 at 9:15
That's correct, we need to turn
[MyTree (m a)]
into [m (MyTree a)]
. We could use map f
for that, for some f :: MyTree (m a) -> m (MyTree a)
. Do we have such a function?– chi
Nov 22 '18 at 9:15
By "Nothing", do you mean the value
Nothing :: Maybe a
? Your function type should be MyTree (Maybe a) -> Maybe (MyTree a)
; no general constraint Monad m =>
is needed.– chepner
Nov 22 '18 at 15:35
By "Nothing", do you mean the value
Nothing :: Maybe a
? Your function type should be MyTree (Maybe a) -> Maybe (MyTree a)
; no general constraint Monad m =>
is needed.– chepner
Nov 22 '18 at 15:35
add a comment |
1 Answer
1
active
oldest
votes
Your check
function is better known as a method of the Traversable
class.
class (Functor t, Foldable t) => Traversable t where
-- The main method
traverse
:: Applicative f
=> (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
-- An alternative
sequenceA
:: Applicative f
=> t (f a) -> f (t a)
sequenceA = traverse id
-- (Mostly) legacy methods
mapM
:: Monad m
=> (a -> m b) -> t a -> m (t b)
mapM = traverse
sequence
:: Monad m
=> t (m a) -> m (t a)
sequence = sequenceA
Specifically, check
is sequence
for MyTree
. So we will get it if we write a Traversable MyTree
instance. But let's first take a step back in two directions. Traversable
is a subclass of both Functor
and Foldable
, and that is no coincidence. It's possible to implement both fmap
and foldMap
using traverse
. But more to the point, the structures of fmap
, foldMap
and traverse
tend to look almost identical! So let's start with those easier ones.
instance Functor MyTree where
fmap f (MyTree a ts) = MyTree (f a) _
What goes in that blank? We have a list of subtrees and we need to generate a new one, so a good bet is
fmap f (MyTree a ts) = MyTree (f a) (fmap _ ts)
Now the blank has type MyTree a -> MyTree b
, so we just call fmap
recursively:
fmap f (MyTree a ts) = MyTree (f a) (fmap (fmap f) ts)
And we're done. Now let's turn to Foldable
.
foldMap f (MyTree a ts) = _
Well, we're going to need to apply f
to a
to get a value in the monoid, then fold up the subtrees and combine the results. This ends up looking quite a bit like fmap
, as promised.
foldMap f (MyTree a ts) = f a <> foldMap (foldMap f) ts
So now we get to Traversable
. It's going to be pretty similar to fmap
, but we need to combine results using Applicative
operations somewhat like we combined foldMap
results using Monoid
operations.
instance Traversable MyTree where
traverse f (MyTree a ts) = _
We have
a :: a
ts :: [MyTree a]
f :: a -> f b
Obviously, we're going to want to apply f
to a
. Following the pattern for fmap
and foldMap
, we're going to calculate traverse (traverse f) ts
. So let's see where that gets us:
traverse f (MyTree a ts) = _ (f a) (traverse (traverse f) ts)
Now GHC will tell us that
_ :: f b -> f [MyTree b] -> f (MyTree b)
We need to take the b
result from the first action and the [MyTree b]
result from the second action and apply the MyTree
constructor to put them together. We can do this using liftA2
:
traverse f (MyTree a ts) = liftA2 MyTree (f a) (traverse (traverse f) ts)
Once you've gotten the hang of writing Functor
, Foldable
, and Traversable
instances, doing so tends to get pretty dull. So GHC has an extension that lets the compiler write them for you.
{-# language DeriveTraversable #-}
module MyModule where
data MyTree a = MyTree a [MyTree a]
deriving (Functor, Foldable, Traversable)
And you're done.
Thanks for the quick and useful answer. I really appreciate it! @DanielWagner
– ykdojo
Nov 22 '18 at 2:36
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53421838%2fstruggling-with-implementing-a-monad-function%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Your check
function is better known as a method of the Traversable
class.
class (Functor t, Foldable t) => Traversable t where
-- The main method
traverse
:: Applicative f
=> (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
-- An alternative
sequenceA
:: Applicative f
=> t (f a) -> f (t a)
sequenceA = traverse id
-- (Mostly) legacy methods
mapM
:: Monad m
=> (a -> m b) -> t a -> m (t b)
mapM = traverse
sequence
:: Monad m
=> t (m a) -> m (t a)
sequence = sequenceA
Specifically, check
is sequence
for MyTree
. So we will get it if we write a Traversable MyTree
instance. But let's first take a step back in two directions. Traversable
is a subclass of both Functor
and Foldable
, and that is no coincidence. It's possible to implement both fmap
and foldMap
using traverse
. But more to the point, the structures of fmap
, foldMap
and traverse
tend to look almost identical! So let's start with those easier ones.
instance Functor MyTree where
fmap f (MyTree a ts) = MyTree (f a) _
What goes in that blank? We have a list of subtrees and we need to generate a new one, so a good bet is
fmap f (MyTree a ts) = MyTree (f a) (fmap _ ts)
Now the blank has type MyTree a -> MyTree b
, so we just call fmap
recursively:
fmap f (MyTree a ts) = MyTree (f a) (fmap (fmap f) ts)
And we're done. Now let's turn to Foldable
.
foldMap f (MyTree a ts) = _
Well, we're going to need to apply f
to a
to get a value in the monoid, then fold up the subtrees and combine the results. This ends up looking quite a bit like fmap
, as promised.
foldMap f (MyTree a ts) = f a <> foldMap (foldMap f) ts
So now we get to Traversable
. It's going to be pretty similar to fmap
, but we need to combine results using Applicative
operations somewhat like we combined foldMap
results using Monoid
operations.
instance Traversable MyTree where
traverse f (MyTree a ts) = _
We have
a :: a
ts :: [MyTree a]
f :: a -> f b
Obviously, we're going to want to apply f
to a
. Following the pattern for fmap
and foldMap
, we're going to calculate traverse (traverse f) ts
. So let's see where that gets us:
traverse f (MyTree a ts) = _ (f a) (traverse (traverse f) ts)
Now GHC will tell us that
_ :: f b -> f [MyTree b] -> f (MyTree b)
We need to take the b
result from the first action and the [MyTree b]
result from the second action and apply the MyTree
constructor to put them together. We can do this using liftA2
:
traverse f (MyTree a ts) = liftA2 MyTree (f a) (traverse (traverse f) ts)
Once you've gotten the hang of writing Functor
, Foldable
, and Traversable
instances, doing so tends to get pretty dull. So GHC has an extension that lets the compiler write them for you.
{-# language DeriveTraversable #-}
module MyModule where
data MyTree a = MyTree a [MyTree a]
deriving (Functor, Foldable, Traversable)
And you're done.
Thanks for the quick and useful answer. I really appreciate it! @DanielWagner
– ykdojo
Nov 22 '18 at 2:36
add a comment |
Your check
function is better known as a method of the Traversable
class.
class (Functor t, Foldable t) => Traversable t where
-- The main method
traverse
:: Applicative f
=> (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
-- An alternative
sequenceA
:: Applicative f
=> t (f a) -> f (t a)
sequenceA = traverse id
-- (Mostly) legacy methods
mapM
:: Monad m
=> (a -> m b) -> t a -> m (t b)
mapM = traverse
sequence
:: Monad m
=> t (m a) -> m (t a)
sequence = sequenceA
Specifically, check
is sequence
for MyTree
. So we will get it if we write a Traversable MyTree
instance. But let's first take a step back in two directions. Traversable
is a subclass of both Functor
and Foldable
, and that is no coincidence. It's possible to implement both fmap
and foldMap
using traverse
. But more to the point, the structures of fmap
, foldMap
and traverse
tend to look almost identical! So let's start with those easier ones.
instance Functor MyTree where
fmap f (MyTree a ts) = MyTree (f a) _
What goes in that blank? We have a list of subtrees and we need to generate a new one, so a good bet is
fmap f (MyTree a ts) = MyTree (f a) (fmap _ ts)
Now the blank has type MyTree a -> MyTree b
, so we just call fmap
recursively:
fmap f (MyTree a ts) = MyTree (f a) (fmap (fmap f) ts)
And we're done. Now let's turn to Foldable
.
foldMap f (MyTree a ts) = _
Well, we're going to need to apply f
to a
to get a value in the monoid, then fold up the subtrees and combine the results. This ends up looking quite a bit like fmap
, as promised.
foldMap f (MyTree a ts) = f a <> foldMap (foldMap f) ts
So now we get to Traversable
. It's going to be pretty similar to fmap
, but we need to combine results using Applicative
operations somewhat like we combined foldMap
results using Monoid
operations.
instance Traversable MyTree where
traverse f (MyTree a ts) = _
We have
a :: a
ts :: [MyTree a]
f :: a -> f b
Obviously, we're going to want to apply f
to a
. Following the pattern for fmap
and foldMap
, we're going to calculate traverse (traverse f) ts
. So let's see where that gets us:
traverse f (MyTree a ts) = _ (f a) (traverse (traverse f) ts)
Now GHC will tell us that
_ :: f b -> f [MyTree b] -> f (MyTree b)
We need to take the b
result from the first action and the [MyTree b]
result from the second action and apply the MyTree
constructor to put them together. We can do this using liftA2
:
traverse f (MyTree a ts) = liftA2 MyTree (f a) (traverse (traverse f) ts)
Once you've gotten the hang of writing Functor
, Foldable
, and Traversable
instances, doing so tends to get pretty dull. So GHC has an extension that lets the compiler write them for you.
{-# language DeriveTraversable #-}
module MyModule where
data MyTree a = MyTree a [MyTree a]
deriving (Functor, Foldable, Traversable)
And you're done.
Thanks for the quick and useful answer. I really appreciate it! @DanielWagner
– ykdojo
Nov 22 '18 at 2:36
add a comment |
Your check
function is better known as a method of the Traversable
class.
class (Functor t, Foldable t) => Traversable t where
-- The main method
traverse
:: Applicative f
=> (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
-- An alternative
sequenceA
:: Applicative f
=> t (f a) -> f (t a)
sequenceA = traverse id
-- (Mostly) legacy methods
mapM
:: Monad m
=> (a -> m b) -> t a -> m (t b)
mapM = traverse
sequence
:: Monad m
=> t (m a) -> m (t a)
sequence = sequenceA
Specifically, check
is sequence
for MyTree
. So we will get it if we write a Traversable MyTree
instance. But let's first take a step back in two directions. Traversable
is a subclass of both Functor
and Foldable
, and that is no coincidence. It's possible to implement both fmap
and foldMap
using traverse
. But more to the point, the structures of fmap
, foldMap
and traverse
tend to look almost identical! So let's start with those easier ones.
instance Functor MyTree where
fmap f (MyTree a ts) = MyTree (f a) _
What goes in that blank? We have a list of subtrees and we need to generate a new one, so a good bet is
fmap f (MyTree a ts) = MyTree (f a) (fmap _ ts)
Now the blank has type MyTree a -> MyTree b
, so we just call fmap
recursively:
fmap f (MyTree a ts) = MyTree (f a) (fmap (fmap f) ts)
And we're done. Now let's turn to Foldable
.
foldMap f (MyTree a ts) = _
Well, we're going to need to apply f
to a
to get a value in the monoid, then fold up the subtrees and combine the results. This ends up looking quite a bit like fmap
, as promised.
foldMap f (MyTree a ts) = f a <> foldMap (foldMap f) ts
So now we get to Traversable
. It's going to be pretty similar to fmap
, but we need to combine results using Applicative
operations somewhat like we combined foldMap
results using Monoid
operations.
instance Traversable MyTree where
traverse f (MyTree a ts) = _
We have
a :: a
ts :: [MyTree a]
f :: a -> f b
Obviously, we're going to want to apply f
to a
. Following the pattern for fmap
and foldMap
, we're going to calculate traverse (traverse f) ts
. So let's see where that gets us:
traverse f (MyTree a ts) = _ (f a) (traverse (traverse f) ts)
Now GHC will tell us that
_ :: f b -> f [MyTree b] -> f (MyTree b)
We need to take the b
result from the first action and the [MyTree b]
result from the second action and apply the MyTree
constructor to put them together. We can do this using liftA2
:
traverse f (MyTree a ts) = liftA2 MyTree (f a) (traverse (traverse f) ts)
Once you've gotten the hang of writing Functor
, Foldable
, and Traversable
instances, doing so tends to get pretty dull. So GHC has an extension that lets the compiler write them for you.
{-# language DeriveTraversable #-}
module MyModule where
data MyTree a = MyTree a [MyTree a]
deriving (Functor, Foldable, Traversable)
And you're done.
Your check
function is better known as a method of the Traversable
class.
class (Functor t, Foldable t) => Traversable t where
-- The main method
traverse
:: Applicative f
=> (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
-- An alternative
sequenceA
:: Applicative f
=> t (f a) -> f (t a)
sequenceA = traverse id
-- (Mostly) legacy methods
mapM
:: Monad m
=> (a -> m b) -> t a -> m (t b)
mapM = traverse
sequence
:: Monad m
=> t (m a) -> m (t a)
sequence = sequenceA
Specifically, check
is sequence
for MyTree
. So we will get it if we write a Traversable MyTree
instance. But let's first take a step back in two directions. Traversable
is a subclass of both Functor
and Foldable
, and that is no coincidence. It's possible to implement both fmap
and foldMap
using traverse
. But more to the point, the structures of fmap
, foldMap
and traverse
tend to look almost identical! So let's start with those easier ones.
instance Functor MyTree where
fmap f (MyTree a ts) = MyTree (f a) _
What goes in that blank? We have a list of subtrees and we need to generate a new one, so a good bet is
fmap f (MyTree a ts) = MyTree (f a) (fmap _ ts)
Now the blank has type MyTree a -> MyTree b
, so we just call fmap
recursively:
fmap f (MyTree a ts) = MyTree (f a) (fmap (fmap f) ts)
And we're done. Now let's turn to Foldable
.
foldMap f (MyTree a ts) = _
Well, we're going to need to apply f
to a
to get a value in the monoid, then fold up the subtrees and combine the results. This ends up looking quite a bit like fmap
, as promised.
foldMap f (MyTree a ts) = f a <> foldMap (foldMap f) ts
So now we get to Traversable
. It's going to be pretty similar to fmap
, but we need to combine results using Applicative
operations somewhat like we combined foldMap
results using Monoid
operations.
instance Traversable MyTree where
traverse f (MyTree a ts) = _
We have
a :: a
ts :: [MyTree a]
f :: a -> f b
Obviously, we're going to want to apply f
to a
. Following the pattern for fmap
and foldMap
, we're going to calculate traverse (traverse f) ts
. So let's see where that gets us:
traverse f (MyTree a ts) = _ (f a) (traverse (traverse f) ts)
Now GHC will tell us that
_ :: f b -> f [MyTree b] -> f (MyTree b)
We need to take the b
result from the first action and the [MyTree b]
result from the second action and apply the MyTree
constructor to put them together. We can do this using liftA2
:
traverse f (MyTree a ts) = liftA2 MyTree (f a) (traverse (traverse f) ts)
Once you've gotten the hang of writing Functor
, Foldable
, and Traversable
instances, doing so tends to get pretty dull. So GHC has an extension that lets the compiler write them for you.
{-# language DeriveTraversable #-}
module MyModule where
data MyTree a = MyTree a [MyTree a]
deriving (Functor, Foldable, Traversable)
And you're done.
edited Nov 22 '18 at 1:32
Daniel Wagner
104k7161285
104k7161285
answered Nov 22 '18 at 0:34
dfeuerdfeuer
33.7k349133
33.7k349133
Thanks for the quick and useful answer. I really appreciate it! @DanielWagner
– ykdojo
Nov 22 '18 at 2:36
add a comment |
Thanks for the quick and useful answer. I really appreciate it! @DanielWagner
– ykdojo
Nov 22 '18 at 2:36
Thanks for the quick and useful answer. I really appreciate it! @DanielWagner
– ykdojo
Nov 22 '18 at 2:36
Thanks for the quick and useful answer. I really appreciate it! @DanielWagner
– ykdojo
Nov 22 '18 at 2:36
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53421838%2fstruggling-with-implementing-a-monad-function%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
3
As a hint, try writing an auxiliary function
[m (MyTree a)] -> m [MyTree a]
first (or usesequence
from the libraries). Then think about how you can exploit that forxs
. You will probably realize that you don't even need to handle the first elementx
in a special way, but you can directly usecheck (MyTree v xs) =...
.– chi
Nov 21 '18 at 23:33
Thanks @chi. One more question!
xs
has a type[MyTree (m a)]
and how do you feed that in an auxiliary function,[m (MyTree a)] -> m [MyTree a]
? I'm getting it to the right way but bridging between these two is quite confusing.– ykdojo
Nov 22 '18 at 2:07
That's correct, we need to turn
[MyTree (m a)]
into[m (MyTree a)]
. We could usemap f
for that, for somef :: MyTree (m a) -> m (MyTree a)
. Do we have such a function?– chi
Nov 22 '18 at 9:15
By "Nothing", do you mean the value
Nothing :: Maybe a
? Your function type should beMyTree (Maybe a) -> Maybe (MyTree a)
; no general constraintMonad m =>
is needed.– chepner
Nov 22 '18 at 15:35