Skip to content

Instantly share code, notes, and snippets.

@dminuoso
Last active January 10, 2018 14:27
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 dminuoso/35bcb3efac3526e8e8d48149cafc6707 to your computer and use it in GitHub Desktop.
Save dminuoso/35bcb3efac3526e8e8d48149cafc6707 to your computer and use it in GitHub Desktop.
Const = Struct.new(:value) do
def fmap(*args)
-> (func) { self }.curry[*args]
end
def self.make(*args)
-> (o) { new(o) }.curry[*args]
end
end
Const.implements Functor
module Functor
extend Protocol
requires_instance_method :fmap
end
module ArityRange
def arity_range
args = parameters.map(&:first)
req = args.count :req
keyreq = args.count :keyreq
opt = args.include?(:rest) ? Float::INFINITY : args.count(:opt)
keyopt = args.include?(:keyrest) ? Float::INFINITY : args.count(:key)
{ arguments: req..req + opt, keywords: keyreq..keyreq + keyopt }
end
refine UnboundMethod do
include ArityRange
end
refine Method do
include ArityRange
end
end
module Protocol
using ArityRange
class MethodRequirement
Exception = Class.new(StandardError)
def initialize(method, arguments: nil)
@method = method
@arguments = case arguments
when NilClass then nil
when Integer then arguments..arguments
when Range then arguments
else raise ArgumentError
end
end
def ===(other)
other.method_defined?(@method) &&
(!@arguments || @arguments == other.instance_method(@method).arity_range[:arguments])
end
def to_s
"method ##{@method}#{" with arity #{@arguments}" if @arguments}"
end
end
class ClassMethodRequirement < MethodRequirement
Exception = Class.new(StandardError)
def ===(other)
other.respond_to?(@method) &&
(!@arguments || @arguments == other.method(@method).arity_range[:arguments])
end
def to_s
"class #{super}"
end
end
class AncestorRequirement
Exception = Class.new(StandardError)
def initialize(ancestor)
@ancestor = ancestor
end
def ===(other)
other < @ancestor
end
def to_s
"ancestor #{@ancestor}"
end
end
def self.extended(mod)
mod.instance_eval do
@requirements = []
end
end
def requires_ancestor(mod)
@requirements << AncestorRequirement.new(mod)
end
def requires_instance_method(method, **kw)
@requirements << MethodRequirement.new(method, **kw)
end
def requires_class_method(method, **kw)
@requirements << ClassMethodRequirement.new(method, **kw)
end
def included(target)
@requirements.each do |requirement|
unless requirement === target
raise requirement.class::Exception, "protocol #{self} requires #{requirement}"
end
end
end
end
class Module
alias_method :implements, :include
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment