Skip to content

Instantly share code, notes, and snippets.

@assembler assembler/monads.rb
Created Oct 13, 2014

Embed
What would you like to do?
monads
# https://www.youtube.com/watch?v=uTR__8RvgvM
# ---------------------------------------------------------------------------- #
Project = Struct.new(:creator)
Person = Struct.new(:address)
Address = Struct.new(:country)
Country = Struct.new(:capital)
City = Struct.new(:weather)
class Optional
attr_accessor :value
def initialize(value)
@value = value
end
def self.from_value(value)
new(value)
end
def and_then(&block)
if value.nil?
Optional.new(nil)
else
block.call(value)
end
end
def method_missing(*args, &block)
and_then do |value|
Optional.new(value.public_send(*args, &block))
end
end
end
def get_project_weather(project)
# Optional.new(project)
# .and_then { |project| Optional.new(project.creator) }
# .and_then { |creator| Optional.new(creator.address) }
# .and_then { |address| Optional.new(address.country) }
# .and_then { |country| Optional.new(country.capital) }
# .and_then { |capital| Optional.new(capital.weather) }
# .value
Optional.new(project).creator.address.country.capital.weather.value
end
project = Project.new(Person.new(Address.new(Country.new(City.new("sunny")))))
p get_project_weather(project)
project = Project.new(Person.new)
p get_project_weather(project)
# ---------------------------------------------------------------------------- #
Blog = Struct.new(:categories)
Category = Struct.new(:posts)
Post = Struct.new(:comments)
class Many
attr_accessor :values
def initialize(values)
@values = values
end
def self.from_value(value)
new([value])
end
def and_then(&block)
Many.new(values.map(&block).flat_map(&:values))
end
def method_missing(*args, &block)
and_then do |value|
Many.new(value.public_send(*args, &block))
end
end
end
def words_in(blogs)
# Many.new(blogs)
# .and_then { |blog| Many.new(blog.categories) }
# .and_then { |category| Many.new(category.posts) }
# .and_then { |post| Many.new(post.comments) }
# .and_then { |comment| Many.new(comment.split(/\s+/)) }
# .values
Many.new(blogs).categories.posts.comments.split(/\s+/).values
end
blogs = [
Blog.new([
Category.new([
Post.new(['I love cats', 'I love dogs']),
Post.new(['I love mice', 'I love pigs']),
]),
Category.new([
Post.new(['I hate cats', 'I hate dogs']),
Post.new(['I hate mice', 'I hate pigs']),
]),
]),
Blog.new([
Category.new([
Post.new(['Red is better than blue']),
]),
Category.new([
Post.new(['Blue is better than red']),
]),
]),
]
p words_in(blogs)
# ---------------------------------------------------------------------------- #
Thread.abort_on_exception = true
require "rubygems"
gem "uri_template"
require "uri"
require "net/http"
require "json"
require "uri_template"
def get_json(url, &success)
Thread.new do
puts "[GET] #{url}..."
uri = URI.parse(url)
json = Net::HTTP.get(uri)
value = JSON.parse(json)
success.call(value)
end
end
# get_json("https://api.github.com") do |urls|
# org_url_template = URITemplate.new(urls["organization_url"])
# org_url = org_url_template.expand(org: "ruby")
# get_json(org_url) do |org|
# repos_url = org["repos_url"]
# get_json(repos_url) do |repos|
# most_popular_repo = repos.max_by { |repo| repo["watchers_count"] }
# repo_url = most_popular_repo["url"]
# get_json(repo_url) do |repo|
# contributors_url = repo["contributors_url"]
# get_json(contributors_url) do |users|
# most_profilic_user = users.max_by { |user| user["contributions"] }
# user_url = most_profilic_user["url"]
# get_json(user_url) do |user|
# puts "The most influential Rubyist is #{user["name"]} (#{user["login"]})"
# end
# end
# end
# end
# end
# end
class Eventually
attr_accessor :block
def initialize(&block)
@block = block
end
def self.from_value(value)
new { |s| s.call(value) }
end
def and_then(&block)
Eventually.new do |success|
run do |value|
block.call(value).run(&success)
end
end
end
def run(&success)
block.call(success)
end
end
def get_github_api_urls
github_root_url = "https://api.github.com"
Eventually.new{ |success| get_json(github_root_url, &success) }
end
def get_org(urls, name)
org_url_template = URITemplate.new(urls["organization_url"])
org_url = org_url_template.expand(org: name)
Eventually.new{ |success| get_json(org_url, &success) }
end
def get_repos(org)
repos_url = org["repos_url"]
Eventually.new { |success| get_json(repos_url, &success) }
end
def get_most_popular_repo(repos)
most_popular_repo = repos.max_by { |repo| repo["watchers_count"] }
repo_url = most_popular_repo["url"]
Eventually.new { |success| get_json(repo_url, &success) }
end
def get_contributors(repo)
contributors_url = repo["contributors_url"]
Eventually.new { |success| get_json(contributors_url, &success) }
end
def get_most_profilic_user(users)
most_profilic_user = users.max_by { |user| user["contributions"] }
user_url = most_profilic_user["url"]
Eventually.new { |success| get_json(user_url, &success) }
end
get_github_api_urls
.and_then { |urls | get_org(urls, 'ruby') }
.and_then { |org | get_repos(org) }
.and_then { |repos| get_most_popular_repo(repos) }
.and_then { |repo | get_contributors(repo) }
.and_then { |users| get_most_profilic_user(users) }
.run do |user|
puts "The most influential Rubyist is #{user["name"]} (#{user["login"]})"
end
# ---------------------------------------------------------------------------- #
module Monad
def within(&block)
and_then do |value|
self.class.from_value(block.call(value))
end
end
end
Optional.include Monad
Many.include Monad
Eventually.include Monad
def description_from(containing_json)
containing_json.within do |json|
JSON.parse(json)
end.within do |hash|
"%s (%s)" % hash.values_at("name", "login")
end
end
optional_nil = Optional.new(nil)
p description_from(optional_nil)
optional_json = Optional.new(%|{ "login": "nobu", "name": "Nobuyoshi Nakada" }|)
p description_from(optional_json)
many_jsons = Many.new([
%|{ "login": "nobu", "name": "Nobuyoshi Nakada" }|,
%|{ "login": "matz", "name": "Yukihiro Matsumoto" }|,
])
p description_from(many_jsons)
eventually_json = Eventually.new do |success|
Thread.new do
uri = URI.parse("https://api.github.com/users/nobu")
json = Net::HTTP.get(uri)
success.call(json)
end
end
description_from(eventually_json).run { |description| p(description) }
puts "\n press any key to exit \n"
gets
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.