Skip to content

Instantly share code, notes, and snippets.

@assembler
Created October 13, 2014 11:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save assembler/6ebafc69d1b92148381a to your computer and use it in GitHub Desktop.
Save assembler/6ebafc69d1b92148381a to your computer and use it in GitHub Desktop.
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