Skip to content

Instantly share code, notes, and snippets.

@elbeno
Last active August 29, 2015 14:20
Show Gist options
  • Save elbeno/a82f9eea521adc38235f to your computer and use it in GitHub Desktop.
Save elbeno/a82f9eea521adc38235f to your computer and use it in GitHub Desktop.
Writer monad idea, with foldable things instead of monoids
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
module Main where
-- Here's the familiar Writer monad. For the purposes here, we're not worried
-- about the instances for functor, monad etc.
newtype Writer a x = Writer { runWriter :: (x, a) }
-- Here's something like a relaxed monoid: an accumulator (a) that can have bs
-- added to it. If a and b are the same, this is a regular monoid of course.
class Foldoid a b | a->b where
fempty :: a
fappend :: a -> b -> a
-- Here's something like a relaxed monad: it has two type variables and a
-- function is allowed to change both. The idea is that if a and b have a
-- Foldoid instance, we can write fbind.
class Foldad m a b where
(>>>=) :: m a x -> (x -> m b y) -> m a y
freturn :: x -> m a x
-- >>>= gets the same precedence/fixity as >>=
infixl 1 >>>=
-- So here's Writer generalised with Foldoid instead of Monoid.
instance (Foldoid a b) => Foldad Writer a b where
(Writer (x, a)) >>>= f = let (Writer (y, b)) = f x
in Writer (y, a `fappend` b)
freturn x = Writer (x, fempty)
-- Here's a simple Foldoid that accumulates the max length of strings you feed
-- it.
instance Foldoid Int String where
fempty = 0
fappend n s = max n (length s)
-- The first 10 natural numbers as words.
numbers :: [String]
numbers = [ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
-- A regular writer that just stores the string of its number.
logNumberLength :: Int -> Writer String Int
logNumberLength x = Writer (x, numbers !! x)
-- Now we can log number lengths and remember the longest.
logLengths :: Writer Int Int
logLengths =
freturn (0 :: Int) >>>=
(\_ -> logNumberLength 3) >>>= -- "three" = length 5
\_ -> logNumberLength 2 -- "two" = length 3
main :: IO ()
main = print $ runWriter logLengths
-- output: (2,5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment