Skip to content

Instantly share code, notes, and snippets.

@mike-burns
Created November 29, 2011 01:55
Show Gist options
  • Save mike-burns/1403001 to your computer and use it in GitHub Desktop.
Save mike-burns/1403001 to your computer and use it in GitHub Desktop.
The IO data type, functor, and monad ... in Ruby
class InputOutput
def initialize(&action)
@action = action
end
private_class_method :new
# return :: (Monad m) => a -> m a
def self.unit(x)
new { x }
end
def self.putChar(c)
new { putc c; nil }
end
def self.putStr(s)
new { print s; nil }
end
def self.putStrLn(s)
new { puts s; nil }
end
def self.print(x)
new { p x }
end
def self.getChar
new { STDIN.getc }
end
def self.getLine
new { gets }
end
def self.getContents
new do
acc = []
line = gets
while !line.nil? do
line = gets
acc << line unless line.nil?
end
acc.join("\n")
end
end
def self.interact(&f)
new do
getContents.withThat {|s| putStr(f.call(s)) }
end
end
def self.readFile(file_path)
new { IO.read(file_path) }
end
def self.writeFile(file_path, s)
new { File.open(file_path, 'w') {|f| f.write(s) } }
end
def self.appendFile(file_path, s)
new { File.open(file_path, 'w+') {|f| f.write(s) }}
end
# (>>) :: (Monad m) => m a -> m b -> m b
def andThen(io)
run_it
io
end
# (>>=) :: (Monad m) => m a -> (a -> m b) -> m b
def withThat(&io)
result = run_it
io.call(result)
end
# fmap :: (Functor f) => (a -> b) -> f a -> f b
def fmap(&f)
withThat {|*x| self.unit(f.call(*x)) }
end
# part of monad, controversially
def fail(s)
raise s
end
# normally this is just `main'
def runIO
run_it
end
private
def run_it
@action.call
end
end
InputOutput.getLine.
withThat {|line| InputOutput.putStrLn(line) }.
andThen(InputOutput.putStrLn("Done")).
runIO
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment