Module: Fr::Monad

Includes:
Functor
Included in:
Array, Either, Maybe, Random, Reader, State, Writer
Defined in:
lib/fr/monad.rb

Instance Method Summary collapse

Methods included from Functor

#void

Instance Method Details

#applyM(mf, ma = nil) ⇒ Object

Monad m => m (a -> b) -> m a -> m b

>> mf = m.unit(lambda{|x| lambda{|y| x + y }})

>> ab = m.applyM(mf) >> ma = m.applyM(ab.call(m.unit 3)) >> mb = ma.call(m.unit 4)

> m.unit(7)



221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/fr/monad.rb', line 221

def applyM(mf, ma = nil)
  rest =
    lambda do |ma|
      bind(mf) do |f|
        bind(ma) do |a|
          unit(f.call(a))
        end
      end
    end

  (ma.nil?) ?
    rest : rest.call(ma)
end

#composeM(f, g = nil, &block) ⇒ Object

Left-to-right Kleisli composition of monads

Monad m => (a -> m b) -> (b -> m c) -> a -> m c

>> m.composeM(lambda{|a| m.unit(a.to_s) },

lambda{|b| m.unit(b.length) }).call(450)

> m.unit(3)



77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/fr/monad.rb', line 77

def composeM(f, g = nil, &block)
  rest =
    lambda do |g|
      lambda do |a|
        bind(f.call(a)) do |b|
          g.call(b)
        end
      end
    end

  (g.nil?) ?
    (block_given?) ?
      rest.call(block) : rest : rest.call(g)
end

#filterM(f, as = nil) ⇒ Object

Monad m => (a -> m Boolean) -> [a] -> m [a]

>> m.filterM(lambda{|a| m.unit(a.even?) }).call()

> m.unit()

Not implemented: >> [0,1,2,3,4].filterM{|a| m.unit(a.even?) }

> m.unit()



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/fr/monad.rb', line 101

def filterM(f, as = nil)
  rec =
    lambda do |as|
      if as.empty?
        unit([])
      else
        a, *as = as
        bind(f.call(a)) do |bool|
          bind(rec.call(as)) do |rest|
            unit(bool ?
              a.cons(rest) : rest)
          end
        end
      end
    end

  (as.nil?) ?
    rec : rec.call(as)
end

#foldM(f, seed = nil, bs = nil) ⇒ Object

Monad m => (a -> b -> m a) -> a -> [b] -> m a

>> m.foldM(lambda{|a,b| m.unit(a | b) }).call(0).call()

> m.unit(7)

Not implemented: >> [1,2,4].foldM(0){|a,b| m.unit(a | b) }

> m.unit(7)



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/fr/monad.rb', line 152

def foldM(f, seed = nil, bs = nil)
  rest =
    lambda do |seed|
      lambda do |bs|
        if bs.empty?
          unit(seed)
        else
          b, *bs = bs
          bind(f.call(seed, b)) do |seed_|
            rec.call(seed_).call(bs)
          end
        end
      end
    end

  (bs.nil?) ?
    (seed.nil?) ?
      rest : rest.call(seed) : rest.call(seed).call(bs)
end

#forM(as, f = nil, &block) ⇒ Object

Monad m => [a] -> (a -> m b) -> m [b]

>> m.forM(){|a| m.unit(a.even?) }

> m.unit()

>> m.forM(, lambda{|a| m.unit(a.even?) })

> m.unit()

>> m.forM().call(lambda{|a| m.unit(a.even?) })

> m.unit()



59
60
61
62
63
64
65
66
67
68
# File 'lib/fr/monad.rb', line 59

def forM(as, f = nil, &block)
  rest =
    lambda do |f|
      sequence(as.map(&block))
    end

  (f.nil?) ?
    (block_given?) ?
      rest.call(block) : rest : rest.call(f)
end

#join(mm) ⇒ Object

Remove one level of monadic structure

Monad m => m (m a) -> m a

>> m.join(m.unit(m.unit(100)))

> m.unit(100)

>> Array.join([,[2],])

> [1,2,3]



29
30
31
# File 'lib/fr/monad.rb', line 29

def join(mm)
  bind(mm){|x| x }
end

#liftM(f = nil, ma = nil, &block) ⇒ Object Also known as: map

Monad m => (a -> b) -> m a -> m b

>> m.liftM(lambda{|x| x.even? }).call(m.unit(30))

> m.unit(true)



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/fr/monad.rb', line 187

def liftM(f = nil, ma = nil, &block)
  rest =
    lambda do |f|
      lambda do |ma|
        bind(ma) do |a|
          unit(f.call(a))
        end
      end
    end

  (ma.nil?) ?
    (block_given?) ?
      rest.call(block).call(f) :# m.liftM(ma){ .. }
      rest.call(f)             :# m.liftM(lambda{ .. }).call(ma)
      rest.call(f).call(ma)     # m.liftM(lambda{ .. }, ma)
end

#mapM(f, as = nil) ⇒ Object

Monad m => (a -> m b) -> [a] -> m [b]

>> m.filter(lambda{|a| m.unit(a.even?) }).call()

> m.unit()



38
39
40
41
42
43
44
45
46
# File 'lib/fr/monad.rb', line 38

def mapM(f, as = nil)
  if as.nil?
    lambda do |as|
      sequence(as.map(&f))
    end
  else
    sequence(as.map(&f))
  end
end

#sequence(ms) ⇒ Object

Perform each action, from left to right

Monad m => [m a] -> m [a]

@todo: Need trampoline for 2500+ ms



10
11
12
13
14
15
16
17
18
# File 'lib/fr/monad.rb', line 10

def sequence(ms)
  ms.inject(unit []) do |prev, curr|
    bind(prev) do |xs|
      bind(curr) do |x|
        unit(xs + [x])
      end
    end
  end
end

#unlessM(condition) ⇒ Object

Monad m => Bool -> m () -> m ()



178
179
180
# File 'lib/fr/monad.rb', line 178

def unlessM(condition)
  # todo
end

#whenM(condition) ⇒ Object

Monad m => Bool -> m () -> m ()



173
174
175
# File 'lib/fr/monad.rb', line 173

def whenM(condition)
  # todo
end

#zipM(f, xs = nil, ys = nil) ⇒ Object

Monad m => (a -> b -> m c) -> [a] -> [b] -> m [c]

>> m.zipM(lambda{|a,b| m.unit(a + b) }).call().call()

> m.unit()

Not implemented: >> [1,2,3].zipM(){|a,b| m.unit(a + b) }

> m.unit()



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/fr/monad.rb', line 130

def zipM(f, xs = nil, ys = nil)
  rest =
    lambda do |xs|
      lambda do |ys|
        sequence(xs.zip(ys).map{|(x,y)| f.call(x, y) })
      end
    end

  (ys.nil?) ?
    (xs.nil?) ?
      rest : rest.call(xs) : rest.call(xs).call(ys)
end