Automatic lifting of infix operators to monadic infix operators
Asked Answered
D

3

9

One of the nice things about Haskell is the ability to use infix notation.

1 : 2 : 3 : []    :: Num a => [a]
2 + 4 * 3 + 5     :: Num a => a

But this power is suddenly and sadly lost when the operator needs to be lifted.

liftM2 (*) (liftM2 (+) m2 m4) (liftM2 (+) m3 m5)
liftM2 (:) m1 (liftM2 (:) m2 (liftM2 (:) m3 mE))

It is possible to define similar operators in order to regain this power

(.*) = liftM2 (*)
(.+) = liftM2 (+)
(.:) = liftM2 (:)

m1, m2, m3, m4, m5 :: Monad m, Num a => m a
mE = return []     :: Monad m => m [a]
m1 .: m2 .: m3 .: mE    :: Monad m, Num a => m [a]
m2 .+ m4 .* m3 .+ m5    :: Monad m, Num a => m a

But it is tedious to need to rename every operator I want to use in a monadic context. Is there a better way? Template Haskell, perhaps?

Dictaphone answered 12/10, 2011 at 15:34 Comment(3)
If the mX are IO or ST actions you probably do not want to execute them multiple times, because liftM2 (+) m m is then very differetn from do n <- m; return n+nBaynebridge
SHE has idiom brackets which let you write (|f m1 m2 ... mn|) for (pure f <*> m1 <*> m2 <*> ... <*> mn) and (|m1 <> m2|) for (pure (<>) <*> m1 <*> m2). I didn't get around to implementing a fixity-resolving lifter for more complex expressions built from infix operators.Zandrazandt
@Baynebridge the semantic meaning is to indeed run the effect multiple times. For example, liftM2 (+) readInt readInt - in this case I would want the readInt action performed twice, rather than only once, since it will possibly grab a different int the second time.Dictaphone
A
9

You can define a new infix lift:

v <. f = liftM2 f v
f .> v = f v

Example use:

[3] <.(+).> [4]

...but I don't know of any real way that isn't 100% annoying.

Albite answered 12/10, 2011 at 16:15 Comment(2)
Yeah...I suppose if the same monadic operaters are used over and over, it is less tedious to simply make my own custom operators.Dictaphone
@Dan, if the same monadic operators are used over and over -- that is, you find yourself lifting the same pure operators into a monad many times, I would venture a guess that your code could stand to be less monadic -- shorter and simpler that way.Hedy
K
10

You can make all monads instances of Num:

{-# LANGUAGE FlexibleInstances, FlexibleContexts, UndecidableInstances #-}

import Control.Monad

instance (Monad m, Num n, Show (m n), Eq (m n)) => Num (m n) where
  (+) = liftM2 (+)
  (*) = liftM2 (*)

Then you can do f.e.:

*Main> [3,4] * [5,6] + [1,2]
[16,17,19,20,21,22,25,26]

But this only works for operators that are defined with type classes. With : this isn't possible.

Kicksorter answered 12/10, 2011 at 15:53 Comment(1)
You'll probably need to add dummy Show and Eq instances for most interesting monads to use this.Albite
A
9

You can define a new infix lift:

v <. f = liftM2 f v
f .> v = f v

Example use:

[3] <.(+).> [4]

...but I don't know of any real way that isn't 100% annoying.

Albite answered 12/10, 2011 at 16:15 Comment(2)
Yeah...I suppose if the same monadic operaters are used over and over, it is less tedious to simply make my own custom operators.Dictaphone
@Dan, if the same monadic operators are used over and over -- that is, you find yourself lifting the same pure operators into a monad many times, I would venture a guess that your code could stand to be less monadic -- shorter and simpler that way.Hedy
W
2

There is the style using ap:

return (:) `ap` Just 1 `ap` Just []

or applicative style:

(:) <$> Just 1 <*> Just []
Worsen answered 12/10, 2011 at 16:56 Comment(2)
This does work, but requires the operators to appear in the prefix position, which is what I would like to avoid.Dictaphone
You can of course define x <%> y = y <$> x. That allows you to write Just 1 <%> (:) <*> Just []. I don't know how to do without those pesky parentheses, though.Laius

© 2022 - 2024 — McMap. All rights reserved.