Last active
December 16, 2015 22:19
-
-
Save avibryant/5506177 to your computer and use it in GitHub Desktop.
Totally impractical and absurd monad syntactic sugar for Ruby, roughly modeled on Scala's for-loop syntax.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'continuation' | |
module Monad | |
def -@ | |
MonadContext.bind(self) | |
end | |
def wrap(val) | |
raise NotImplementedError | |
end | |
def bind(&b) | |
raise NotImplementedError | |
end | |
end | |
class Maybe | |
include Monad | |
end | |
class Just < Maybe | |
def get | |
@val | |
end | |
def initialize(val) | |
@val = val | |
end | |
def wrap(val) | |
Just.new(val) | |
end | |
def bind(&b) | |
b.call(@val) | |
end | |
def to_s | |
"Just(#{@val})" | |
end | |
end | |
class None < Maybe | |
@@inst = self.new | |
def wrap(val) | |
@@inst | |
end | |
def bind(&b) | |
@@inst | |
end | |
def to_s | |
"None" | |
end | |
end | |
module Enumerable | |
#for some reason we have to copy this here instead of including Monad | |
def -@ | |
MonadContext.bind(self) | |
end | |
def wrap(val) | |
take(1).map{|ignore| val} | |
end | |
def bind(&b) | |
flat_map(&b) | |
end | |
end | |
def for!(&b) | |
MonadContext.new.run(&b) | |
end | |
class MonadContext | |
def run(&b) | |
self.class.stack << self | |
begin | |
callcc do |cc| | |
@cc_stack = [cc] | |
@coll_stack = [] | |
res = b.call | |
wrapped = @coll_stack[-1].wrap(res) | |
@cc_stack.pop.call(wrapped) | |
end | |
ensure | |
self.class.stack.pop | |
end | |
end | |
def bind(coll) | |
@coll_stack << coll | |
callcc do |cc| | |
res = coll.bind do |el| | |
callcc do |cc2| | |
@cc_stack << cc2 | |
cc.call(el) | |
end | |
end | |
@coll_stack.pop | |
@cc_stack.pop.call(res) | |
end | |
end | |
def self.bind(coll) | |
stack[-1].bind(coll) | |
end | |
def self.stack | |
Thread.current[:MonadContext] ||= [] | |
end | |
end | |
if __FILE__ == $0 | |
res = for! do | |
x =- (1..5) | |
y =- (x..5) | |
z =- (y % 2 == 0 ? None.new : Just.new(1)) | |
y + z | |
end | |
p res | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment