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?