Created
October 13, 2014 11:06
-
-
Save assembler/6ebafc69d1b92148381a to your computer and use it in GitHub Desktop.
monads
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
# 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