Last active
May 17, 2016 00:53
-
-
Save stefanoc/b4a9ab4c9516123d6bd7 to your computer and use it in GitHub Desktop.
Rusty monads. Inspired by http://www.codethatgrows.com/lessons-learned-from-rust-the-result-monad/
This file contains hidden or 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
class Option | |
private def initialize(value) | |
@value = value | |
end | |
def self.Some(value) | |
new(value) | |
end | |
None = new(nil) | |
def to_s | |
case @value | |
when nil then "None" | |
else "Some(#{@value})" | |
end | |
end | |
def some? | |
@value != nil | |
end | |
def none? | |
@value == nil | |
end | |
def unwrap | |
some? ? @value : fail("Panic") | |
end | |
def map | |
some? ? Maybe(yield @value) : None | |
end | |
def map_or(default) | |
some? ? yield(@value) : default | |
end | |
def unwrap_or(default) | |
some? ? @value : default | |
end | |
def unwrap_or_else | |
some? ? @value : yield | |
end | |
def ok_or(err) | |
some? ? Result::Ok(@value) : Result::Err(err) | |
end | |
def ok_or_else | |
some? ? Result::Ok(@value) : Capture { yield } | |
end | |
def and(optb) | |
some? ? Maybe(optb) : None | |
end | |
def and_then | |
some? ? Maybe(yield @value) : None | |
end | |
def or(optb) | |
some? ? self : Maybe(optb) | |
end | |
def or_else | |
some? ? self : Maybe(yield @value) | |
end | |
end | |
class Result | |
private def initialize(value, err) | |
@value = value | |
@err = err | |
end | |
def self.Ok(value) | |
new(value, nil) | |
end | |
def self.Err(err) | |
new(nil, err) | |
end | |
def to_s | |
if @err | |
"Err(#{@err})" | |
else | |
"Ok(#{@value})" | |
end | |
end | |
def ok? | |
!!@value | |
end | |
def err? | |
!!@err | |
end | |
def ok | |
ok? ? Option::Some(@value) : Option::None | |
end | |
def err | |
ok? ? Option::None : Option::Some(@err) | |
end | |
def map | |
ok? ? Capture { yield(@value) } : self | |
end | |
alias and_then map | |
def map_err | |
ok? ? self : capture_err { yield(@err) } | |
end | |
alias or_else map_err | |
def and(res) | |
ok? ? Capture { res } : self | |
end | |
alias & and | |
def or(res) | |
ok? ? self : Capture { res } | |
end | |
alias | or | |
def unwrap | |
ok? ? @value : fail("Panic") | |
end | |
def unwrap_or(optb) | |
ok? ? @value : optb | |
end | |
def unwrap_or_else | |
ok? ? @value : yield(@err) | |
end | |
def expect(msg) | |
ok? ? @value : fail(msg) | |
end | |
def unwrap_err | |
ok? ? fail("Panic") : @err | |
end | |
private | |
def capture_err(&block) | |
Err(block.call) | |
rescue => e | |
Err(e) | |
end | |
end | |
def Maybe(value) | |
case value | |
when Option then value | |
when nil then Option::None | |
else Option::Some(value) | |
end | |
end | |
def Result(value) | |
case value | |
when Result then value | |
when StandardError then Result::Err(value) | |
else Result::Ok(value) | |
end | |
end | |
def Capture | |
Result(yield) | |
rescue => e | |
Result::Err(e) | |
end | |
def Ok(_); Result::Ok(_) end | |
def Err(_); Result::Err(_) end | |
def Some(_); Option::Some(_) end | |
None = Option::None | |
# Examples | |
def read_line #-> Result<Option<String>, _> | |
Capture { Maybe(STDIN.gets) } | |
end | |
# @value Option<String> | |
def strip_line(value) #-> Option<String> | |
value.and_then { |line| line.chomp } | |
end | |
# @str Option<String> | |
def reverse_it(str) #-> Result<String, _> | |
Capture { str.and_then(&:reverse) } | |
end | |
# @str Option<String> | |
def capitalize_it(str) #-> Option<String, _> | |
str.and_then(&:capitalize) | |
end | |
puts read_line | |
.map { |v| strip_line(v) } | |
.map { |v| reverse_it(v) } | |
.map { |v| capitalize_it(v) } | |
#=> Ok(C) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment