0

In an effort to understand monads better, I'm attempting to write my own. I'm starting with some non-monadic code, and could use some help translating it into a monad.

Basic idea for this contrived example: for each integer result of a computation, I'd like to track if that integer is even or odd. For example, in 4 + 5 = 9, we might return (9, Odd).

I'd like to be able to chain/compose the calculations with >>=. For example:

return 1 >>= (+2) >>= (+5) >>= (+7) =result=> (15, Odd)

Right now, I have the following non-monadic code:

data Quality = Odd | Even deriving Show
qual :: Integer -> Quality
qual x = case odd x of
 True -> Odd
 _ -> Even
type Qualifier = (Integer, Quality)
mkQ :: Integer -> Qualifier
mkQ x = (x, qual x)
plusQ :: Qualifier -> Qualifier -> Qualifier
plusQ (x, _) (y, _) = (x+y, qual (x+y))
chain = plusQ (mkQ 7) . plusQ (mkQ 5) . plusQ (mkQ 2)

What are some ways I can translate the above code into a monad? What are some of the patterns I should look for, and what are common translation patterns for them?

Many thanks in advance!

asked Jan 13, 2014 at 5:51
5
  • 2
    This does not easily admit a monadic translation. Monads are parameterized by a type, and make sense for any type. Yor computation only makes sense for intrgers. Commented Jan 13, 2014 at 6:00
  • 1
    It seems like you're trying to model state transformations / actions, where your state in this case is the Quality. You'll want to look at the State Monad, where your state monad is parameterized by Quality and your operations (+1) would turn into state actions. Commented Jan 13, 2014 at 6:16
  • @n.m. Good point, and one that I was beginning to see and wonder about. What might be a better example of a simple, generic process that would fit an exercise like this? Commented Jan 13, 2014 at 16:24
  • @weirdcanada I was wondering about either using State or Writer monads for this... Writer because the Even/Odd aspect seemed almost like logging. But I was really trying to write my own monad in order to understand the inner and outer workings of monads better. Any ideas? Commented Jan 13, 2014 at 16:26
  • One simple monad represents computations that may end in an error. Described everywhere, but it's still entertaining to come up with your own implementation. Commented Jan 13, 2014 at 17:23

2 Answers 2

3

I think what you actually want is a Num instance for Qualified:

data Qualified = Qualified { isEven :: Bool, value :: Integer }
instance Num Qualified where
 (Qualified e1 n1) + (Qualified e2 n2) = Qualified e (n1 + n2)
 where
 e = (e1 && e2) || (not e1 && not e2)
 (Qualified e1 n1) * (Qualified e2 n2) = Qualified (e1 || e2) (n1 * n2)
 abs (Qualified e n) = Qualified e (abs n)
 signum (Qualified e n) = Qualified e (signum n)
 fromInteger n = Qualified (even n) n

This lets you manipulate Qualified numbers directly using math operators:

>>> let a = fromInteger 3 :: Qualified
>>> let b = fromInteger 4 :: Qualified
>>> a
Qualified {isEven = False, value = 3}
>>> b
Qualified {isEven = True, value = 4}
>>> a + b
Qualified {isEven = False, value = 7}
>>> a * b
Qualified {isEven = True, value = 12}
answered Jan 13, 2014 at 6:16
Sign up to request clarification or add additional context in comments.

5 Comments

OP probably wants a Num instance, but I think the broader goal is that the OP wants to learn and understand monads.
I worry that teaching monads in the context of this specific problem would do more harm than good.
@GabrielGonzalez good concern, and concern acknowledged. What might be a better simple type of monad one could write from scratch to get a better sense of monads?
The State monad is pretty simple, if you want to learn about monads, define your own State monad and then maybe try to write your own parser using your new monad.
@GabrielGonzalez Agreed.
0

Lots of learning from this one. Many thanks to the commentors and answers for your time and guidance!

To summarize:

Solution: As @n.m. and others commented, there isn't a good monad translation for this example because my original model isn't type-generic. Monads are best for type-generic computation patterns. Good examples given include the Maybe monad for computations which may fail, and State monad for storing and carrying along accessory state information through a computation chain.

As an alternate solution, @GabrielGonzalez offered a great solution using type instancing. This keeps the inherent type-specificity of my original model, but broadens its interface to support more of the Num type class interface and clean up the functional interactions.

Next steps: As @weirdcanada and others recommended, I think I'll go play with the State monad and see how I can apply it to this particular example. Then I may try my hand at a custom definition of Maybe as @n.m. recommended.

Again, many thanks to those who commented and responded!

answered Jan 14, 2014 at 17:34

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.