I think I just stumbled onto something thanks to Elegantbeef in the discord. I
can use closure, but more for fetching values from "parent"-Observables (to
erase their types) than the observers.
type Observable[SOURCE] = ref object
value: SOURCE
getValue: proc(): SOURCE {.closure.}
observer: seq[proc(value: SOURCE)]
proc newObservable[SOURCE](value: SOURCE): Observable[SOURCE] =
Observable[SOURCE](
value: value,
getValue: proc(): SOURCE = value
)
type OperatorObservable[SOURCE, RESULT] = ref object
getValue: proc(): RESULT {.closure.}
transformer: proc(value: SOURCE): RESULT
observer: seq[proc(value: RESULT)]
proc newOperatorObservable[A, B, C](
parent: OperatorObservable[A, B],
transformer: proc(value: B): C
): OperatorObservable[B, C] =
proc getValueClosure(): C =
echo "Getting value from Operator Obs"
let parentValue: B = parent.getValue()
return transformer(parentValue)
return OperatorObservable[B, C](
getValue: getValueClosure,
transformer: transformer,
observer: @[]
)
proc newOperatorObservable[A, B](
parent: Observable[A],
transformer: proc(value: A): B
): OperatorObservable[A, B] =
proc getValueClosure(): B =
echo "Getting value from Source Obs"
let parentValue: A = parent.getValue()
return transformer(parentValue)
return OperatorObservable[A, B](
getValue: getValueClosure,
transformer: transformer,
observer: @[]
)
let obs = newObservable[int](5)
let doubleObs = newOperatorObservable(
obs,
proc(x: int): int =
echo "Double: ", x * 2
return x * 2
)
let quadrupleObs = newOperatorObservable(
doubleObs,
proc(x: int): int =
echo "Quadruple: ", x * 2
return x * 2
)
let toStrObs = newOperatorObservable(
quadrupleObs,
proc(x: int): string = "I am now a string! "
)
echo quadrupleObs.getValue()
echo toStrObs.getValue()
Run
I'll see whether this will drive me into a corner somewhere, but I'm feeling
optimistic about this approach.