Free

This repo is based on my blog post on Free Monads in swift.

TL;DR

import Free

struct PrintLine : Symbol {
  typealias Meaning = Void
  let message : String
}

struct GetLine : Symbol {
  typealias Meaning = String
}


let example = PrintLine(message: "What is the answer to life, the universe and everything?")
                          .flatMap(GetLine.init)
                          .map{(answer) -> PrintLine in 
                                PrintLine(message: Int(answer) == 42 ? "Yay!" : "Nope...")
                            }
                            
// now interpret

protocol AsyncInterpretation : Interpretation {

   func runAsync() async -> Meaning

}

extension PrintLine : AsyncInterpretation {

   func runAsync() {
      print(message)
   }

}


extension GetLine : AsyncInterpretation {

  func runAsync() async -> String {
     
     await Task.sleep(nanoseconds: UInt64(1e9 * 60 * 60 * 24 * 365.25 * 7.5 * 1e9))
     return "42"
     
  }

}


func test() {

  Task.detached {
    await example.runAsync()
  }

}

Installation

Step 1: Add this package as a dependency
Step 2: Install Sourcery
Step 3: Read a tutorial how to run sourcery. The most important options are

  • manually
  • as a deamon
  • as a run script phase before compile sources (this is my preferred option as you only have to set this up once)
  • with commandline args
  • with a yaml file
    Step 4: Copy+paste the below template into a .stencil file that you use as your template for code generation (you can fine tune this to your needs):



import Free

public protocol Interpretation : Symbol {}


extension Pure : Interpretation {}
extension Map : Interpretation {}
extension FlatMap : Interpretation {}
extension Recursive : Interpretation {}
extension Either : Interpretation {}
extension Either3 : Interpretation {}
extension Either4 : Interpretation {}
extension Either5 : Interpretation {}

{% for proto in types.protocols|public %}{% if proto.based.Interpretation %}

extension Pure : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        .pure(meaning)
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        .pure(meaning)
    }
    {%endfor%}
    
}


extension Map : {{proto.name}} where Base : {{proto.name}} {
    {% for variable in proto.variables|instance %}
    
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        base.{{variable.name}}.map(transform)
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        base.{{method.name}}.map(transform)
    }
    {%endfor%}
    
}


extension FlatMap : {{proto.name}} where Base : {{proto.name}}, Next : {{proto.name}} {
    {% for variable in proto.variables|instance %}
    
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        base.{{variable.name}}
        .flatMap{transform($0).{{variable.name}}}
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        base.{{method.name}}
        .flatMap{transform($0).{{method.name}}}
    }
    {%endfor%}
    
}


extension Recursive : {{proto.name}} where Recursion : {{proto.name}}  {
    
    {% for variable in proto.variables|instance %}
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .pure(let value):
            return .pure(value)
        case .semiPure(let value):
            return value.{{variable.name}}
        case .recursive(let rec, let transform):
            return rec.{{variable.name}}
            .flatMap{transform($0).{{variable.name}}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .pure(let value):
            return .pure(value)
        case .semiPure(let value):
            return value.{{method.name}}
        case .recursive(let rec, let transform):
            return rec.{{method.name}}
            .flatMap{transform($0).{{method.name}}}
        }
        
    }
    {%endfor%}
    
}


extension Either : {{proto.name}} where S1 : {{proto.name}}, S2 : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .either(let s1):
            return s1.{{variable.name}}
        case .or(let s2):
            return s2.{{variable.name}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .either(let s1):
            return s1.{{method.name}}
        case .or(let s2):
            return s2.{{method.name}}
        }
    }
    {%endfor%}
    
}


extension Either3 : {{proto.name}} where S1 : {{proto.name}}, S2 : {{proto.name}}, S3 : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{variable.name}}
        case .second(let s2):
            return s2.{{variable.name}}
        case .third(let s3):
            return s3.{{variable.name}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{method.name}}
        case .second(let s2):
            return s2.{{method.name}}
        case .third(let s3):
            return s3.{{method.name}}
        }
    }
    {%endfor%}
    
}


extension Either4 : {{proto.name}} where S1 : {{proto.name}}, S2 : {{proto.name}},
S3 : {{proto.name}}, S4 : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{variable.name}}
        case .second(let s2):
            return s2.{{variable.name}}
        case .third(let s3):
            return s3.{{variable.name}}
        case .fourth(let s4):
            return s4.{{variable.name}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{method.name}}
        case .second(let s2):
            return s2.{{method.name}}
        case .third(let s3):
            return s3.{{method.name}}
        case .fourth(let s4):
            return s4.{{method.name}}
        }
    }
    {%endfor%}
    
}


extension Either5 : {{proto.name}} where S1 : {{proto.name}}, S2 : {{proto.name}},
S3 : {{proto.name}}, S4 : {{proto.name}} , S5 : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{variable.name}}
        case .second(let s2):
            return s2.{{variable.name}}
        case .third(let s3):
            return s3.{{variable.name}}
        case .fourth(let s4):
            return s4.{{variable.name}}
        case .fifth(let s5):
            return s5.{{variable.name}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{method.name}}
        case .second(let s2):
            return s2.{{method.name}}
        case .third(let s3):
            return s3.{{method.name}}
        case .fourth(let s4):
            return s4.{{method.name}}
        case .fifth(let s5):
            return s5.{{method.name}}
        }
    }
    {%endfor%}
    
}

{% endif %}{% endfor %}
{% for proto in types.protocols|internal %}{% if proto.based.Interpretation %}

extension Pure : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @usableFromInline
    var {{variable.name}} : {{variable.typeName}} {
        .pure(meaning)
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @usableFromInline
    func {{method.name}} -> {{method.returnTypeName}} {
        .pure(meaning)
    }
    {%endfor%}
    
}


extension Map : {{proto.name}} where Base : {{proto.name}} {
    {% for variable in proto.variables|instance %}
    
    @usableFromInline
    var {{variable.name}} : {{variable.typeName}} {
        base.{{variable.name}}.map(transform)
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @usableFromInline
    func {{method.name}} -> {{method.returnTypeName}} {
        base.{{method.name}}.map(transform)
    }
    {%endfor%}
    
}


extension FlatMap : {{proto.name}} where Base : {{proto.name}}, Next : {{proto.name}} {
    {% for variable in proto.variables|instance %}
    
    @usableFromInline
    var {{variable.name}} : {{variable.typeName}} {
        base.{{variable.name}}
        .flatMap{transform($0).{{variable.name}}}
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @usableFromInline
    func {{method.name}} -> {{method.returnTypeName}} {
        base.{{method.name}}
        .flatMap{transform($0).{{method.name}}}
    }
    {%endfor%}
    
}


extension Recursive : {{proto.name}} where Recursion : {{proto.name}}  {
    
    {% for variable in proto.variables|instance %}
    @usableFromInline
    var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .pure(let value):
            return .pure(value)
        case .semiPure(let value):
            return value.{{variable.name}}
        case .recursive(let rec, let transform):
            return rec.{{variable.name}}
            .flatMap{transform($0).{{variable.name}}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @usableFromInline
    func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .pure(let value):
            return .pure(value)
        case .semiPure(let value):
            return value.{{method.name}}
        case .recursive(let rec, let transform):
            return rec.{{method.name}}
            .flatMap{transform($0).{{method.name}}}
        }
        
    }
    {%endfor%}
    
}


extension Either : {{proto.name}} where S1 : {{proto.name}}, S2 : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @usableFromInline
    var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .either(let s1):
            return s1.{{variable.name}}
        case .or(let s2):
            return s2.{{variable.name}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @usableFromInline
    func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .either(let s1):
            return s1.{{method.name}}
        case .or(let s2):
            return s2.{{method.name}}
        }
    }
    {%endfor%}
    
}


extension Either3 : {{proto.name}} where S1 : {{proto.name}}, S2 : {{proto.name}}, S3 : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @usableFromInline
    var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{variable.name}}
        case .second(let s2):
            return s2.{{variable.name}}
        case .third(let s3):
            return s3.{{variable.name}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @usableFromInline
    func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{method.name}}
        case .second(let s2):
            return s2.{{method.name}}
        case .third(let s3):
            return s3.{{method.name}}
        }
    }
    {%endfor%}
    
}


extension Either4 : {{proto.name}} where S1 : {{proto.name}}, S2 : {{proto.name}},
S3 : {{proto.name}}, S4 : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @usableFromInline
    var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{variable.name}}
        case .second(let s2):
            return s2.{{variable.name}}
        case .third(let s3):
            return s3.{{variable.name}}
        case .fourth(let s4):
            return s4.{{variable.name}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @usableFromInline
    func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{method.name}}
        case .second(let s2):
            return s2.{{method.name}}
        case .third(let s3):
            return s3.{{method.name}}
        case .fourth(let s4):
            return s4.{{method.name}}
        }
    }
    {%endfor%}
    
}


extension Either5 : {{proto.name}} where S1 : {{proto.name}}, S2 : {{proto.name}},
S3 : {{proto.name}}, S4 : {{proto.name}} , S5 : {{proto.name}} {
    
    {% for variable in proto.variables|instance %}
    @usableFromInline
    var {{variable.name}} : {{variable.typeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{variable.name}}
        case .second(let s2):
            return s2.{{variable.name}}
        case .third(let s3):
            return s3.{{variable.name}}
        case .fourth(let s4):
            return s4.{{variable.name}}
        case .fifth(let s5):
            return s5.{{variable.name}}
        }
        
    }
    {%endfor%}
    {% for method in proto.methods|instance %}
    
    @usableFromInline
    func {{method.name}} -> {{method.returnTypeName}} {
        
        switch self {
        case .first(let s1):
            return s1.{{method.name}}
        case .second(let s2):
            return s2.{{method.name}}
        case .third(let s3):
            return s3.{{method.name}}
        case .fourth(let s4):
            return s4.{{method.name}}
        case .fifth(let s5):
            return s5.{{method.name}}
        }
    }
    {%endfor%}
    
}

{% endif %}{% endfor %}

GitHub

View Github