3

I have a function that looks like this

transition :: State -> ([State], [State])

Given the particular domain of my problem, I know how to chain together two successive transition function calls, something like this:

transition `chain` trainsition ... `chain` transition

However, I would like to express this as a Monoid and perform chaining with <> and mappend. Unfortunately, I cannot seem to get the following, or similar variants of, to work:

instance Monoid (State -> ([State], [State])) where
    mempty  = ...
    mappend = ...

The error returned is as follows:

• Illegal instance declaration for
    ‘Monoid (State -> ([State], [State]))’
    (All instance types must be of the form (T a1 ... an)
     where a1 ... an are *distinct type variables*,
     and each type variable appears at most once in the instance head.
     Use FlexibleInstances if you want to disable this.)
• In the instance declaration for
    ‘Monoid (State -> ([State], [State]))’

In general, how can functions be expressed as instances of Monoid?

Anton Xue
  • 813
  • 11
  • 25
  • What happens if you follow the advice given in the error message? – jakubdaniel Jun 27 '17 at 14:51
  • I'm guessing it means that you would need a wrapper for this function, in the form of a data constructor or newtype. However, even then, Monoids such as this one exist: https://stackoverflow.com/a/44344821/2704964. How were they able to use a function type in the instance? – Anton Xue Jun 27 '17 at 14:53
  • They are parametric, if you start mixing concrete types in, you either need to wrap your type or try allowing the `FlexibleInstances` extension. – jakubdaniel Jun 27 '17 at 14:56
  • I tried the following and it somehow compiles, but I don't understand why: I declared `newtype Wrapper a = Wrapper (a -> ([a], [a]))` and then I have my `transition :: State -> ([State], [State])`. Doing this compiles: `transition 'mappend' transition`. How did Haskell know to delegate to the appropriate `mappend`? – Anton Xue Jun 27 '17 at 15:15
  • Did it? as the question you linked suggests, there is an instance of `Monoid` for the arrow type as long as the return type is `Monoid`. You can see that `(a, b)` is Monoid if both `a` and `b` are Monoids, and thus maybe Haskell still could pick a different instance from what you think it picked. – jakubdaniel Jun 27 '17 at 15:19
  • The compositions of such functions would be a monadic bind. You might consider creating an instance of `Monad` for a `newtype` wrapper of `([a],[a])`. Composition of functions like `a -> m a` (where `m` is your wrapper type) would then be handled by `>=>`. – 4castle Jun 28 '17 at 03:30
  • Thanks for the help! I managed to make a `Monad` version of it as well after some tweaking. – Anton Xue Jun 28 '17 at 07:41

1 Answers1

7

Functions are already instances of monoids in a different way. How do you expect Haskell to decide to use that instance or your instance? The usual way of solving your problem is to declare a newtype wrapper such as

newtype Transition a = Transition { runTransition :: a -> ([a], [a]) }

Then, you can make your monoid instance just fine:

instance Monoid (Transition a) where
  mempty  = ...
  mappend = ...

After you are done this, you can may even find foldMap useful. Instead of writing something like

runTransition (Transition  transition `chain`
               Transition  transition `chain`
               ...
               Transition  transition)

You can use foldMap

runTransition (foldMap Transition [transition, transition, ... transition])
Alec
  • 31,829
  • 7
  • 67
  • 114
  • I'm reflecting on this much later now, and after trying several things out, it suddenly makes a lot of sense: you can only use `mappend` on "wrapped" objects (such as `newtype`), which means that you can't arbitrarily do: `transition \`mappend\` transition`. Those get delegated to one of the `Monoid` implementations that come with the Prelude. Rather, you have to do, as you wrote: `(TransitionT transition) \`mappend\` (TransitionT transition)`. Thank you! – Anton Xue Jun 27 '17 at 23:20