Generics and Optionals in Protocols

I have a couple of days off, so I thought I would try my hand at the Protocol Oriented Programming.

One of the first projects I built in Swift was a set of protocols and classes representing expressions. I had 3 types of expressions: MathExpression, StringExpression, and BooleanExpression. These were protocols which inherited from a common Expression protocol. I then had classes representing various operations, etc....

This worked fine, but there was A LOT of repeated code. Basically every type of expression had nearly identical code except for the types (e.g. String vs Bool). I am trying to take advantage of the swift 2.0 features to improve this (and potentially move everything to value types at the same time). The biggest problem I am running into is that protocols with self/associated types can't be used as types themselves. I am finally starting to understand why this is the way it is, but working around it requires a lot of thought.

I would like to have a single Expression protocol which can be used for all of the types, but still have a way to check which type the expression will spit out when calculated. That way I can build up expressions of the same type (e.g. 2*(2+3)) but reject expressions which are trying to mix types in unexpected places (e.g. 2+"2").


The nieve version would look something like this:

protocol Expression {
     typealias resultType //e.g. Int,String,Bool
     var exprValue:exprType {get} //Calculate the end result
     func simplified()->Expression //Simplify if possible
}


But, of course, this can't be used as a type, so there is no way to put the pieces together:

struct OperationExpression<T>:Expression{
     typealias resultType = T
     var lhs:Expression  //ERROR: Expression can only be used as a constraint
     var rhs:Expression
     var op:(resultType,resultType)->resultType
     var exprValue:exprType {
          return op(lhs.exprValue,rhs.exprValue)
     }
}


I really want to be able to do something like:

var lhs:Expression where lhs.resultType == T


Instead I have to do this horrible thing:

protocol Expression {
    typealias resultType
    var exprValue:resultType {get}
}

struct ExpressionOf<T>:Expression {
    typealias resultType = T
    private var _value:()->resultType
    var exprValue:resultType {return _value()}
    init<E:Expression where E.resultType == T>(_ expr:E){
        self._value = {expr.exprValue}
    }
}

struct OperationExpression<T>:Expression {
    typealias resultType = T
    let lhs:ExpressionOf<T>
    let rhs:ExpressionOf<T>
    let operation:(resultType,resultType)->resultType

    var exprValue:resultType {return operation(lhs.exprValue,rhs.exprValue)}

    init<E:Expression where E.resultType == T>(lhs:ExpressionOf<T>,rhs:ExpressionOf<T>, operation:(resultType,resultType)->resultType){
        self.lhs = lhs
        self.rhs = rhs
        self.operation = operation
    }
    init<E1:Expression, E2:Expression where E1.resultType == T, E2.resultType == T>(lhs:E1,rhs:E2, operation:(resultType,resultType)->resultType){
        let left = ExpressionOf(lhs)
        let right = ExpressionOf(rhs)
        self.init(lhs: left, rhs: right, operation: operation)
    }
}


This seems to work, but I am not really happy with it. Does anyone have a better or more elegant way to do this?


Also, when I try to use an optional type (e.g. Int?) for the resultType it won't work. It seems to claim that T will never be nil, but I know optional generic types must be possible since you can have optionals in Arrays. What am I missing here? How can I know if the resultType is optional?

Replies

In your example you can simplify the ExpressionOf struct:


struct ExpressionOf<T>:Expression {
  typealias resultType = T
  let exprValue:resultType
  init<E:Expression where E.resultType == T>(_ expr:E){
  exprValue:resultType = {expr.exprValue}
  }
  }


If you need an internal setter use the "private set" keyword.


Does OperationExpression have to be a struct?

As a function it looks a lot simpler:


func OperationExpression<T,Expr1:Expression,Expr2:Expression where Expr1.resultType==T,Expr2.resultType==T>(expr1:Expr1,expr2:Expr2,op:(T,T)->T)->T
  {
  return op(expr1.exprValue,expr2.exprValue)
  }


Optional form:


protocol Expression { 
typealias resultType
var exprValue:resultType? {get}

func OperationExpression<T, Expr1:Expression,Expr2:Expression where Expr1. resultType==T,Expr2. resultType==T>(expr1:Expr1,expr2:Expr2,op:(T,T)->T)->T?
 {
 var result:T?
 if let value1 = expr1. exprValue, let value2 = expr2. exprValue {
 result = op(value1, value2)
 }
return result
}