No way to create a container of polymorphic-open records.
samrushing opened this issue · comments
There is currently no way to create a list (or other container) of records with a restricted/open row type. It appears that a 'subtyping' cast operator is needed. This sample illustrates the problem:
(include "lib/basis.scm")
(typealias ralias (rproduct (rlabel f0 (pre int) 'a)))
(define (cast r)
(%%cexp ((ralias 'a) -> (ralias 'b)) "%0" r)
)
(define (dump-poly pl)
(for-range i (length pl)
(let ((item (nth pl i)))
(printf (int i) " = " (int item.f0) "\n")
)))
(define poly-list
(cons (cast {f1=1})
(cons (cast {f0=2 xyz=2})
(cons (cast {f0=3 abc="hi"})
(list:nil)))))
(dump-poly poly-list)
I intend to revisit this issue later.
This mechanism in the code seems to work. By using %%cexp
and casting both sides of it it to the desired cast-type, it forces the inference to unify both sides with the cast-type yet keeps a "boundary" in the middle across which the unification doesn't pass.
However, I can't seem to formulate a cast-to
macro which accepts the type as an argument. Even if I could, the compile error if the macro wasn't used properly would be pretty bad, so it justifies something better.
NOTE: In theory I think it should be possible to do this hetro-unification automatically. It requires encoding
%rextend
as (pre) and%raccess
as (abs). Then during unify, when unifying rows(abs 'a U _ 'a) -> (abs 'a)
;(pre 'a U pre 'a) -> (pre 'a)
;(pre 'a U ...) -> (...)
;(abs 'a U ...) -> type-error
In english, if either row has an
(abs a)
field, the both rows must have ana
field and the resulting type includes(abs a)
; if both rows have(pre a)
then the resulting type has(pre a)
, if only one row has(pre a)
then the resulting type has noa
field; and if one row has an(abs a)
field, and the other row is i missing ana
field, it's a type error.
(include "lib/basis.scm")
(typealias generic_row {field=int ...})
;; runtime polymorphic existential list
(typealias generic_list (list {field=int ...}) )
;; instantiate a runtime polymorphic ( existential ) list
(define a_var : (list {field=int ...})
(list:nil))
(defmacro cast (cast x) -> (%%cexp ( 'generic_row -> 'generic_row ) "%0" x))
; i tried to send the type as a parameter to macro, but it doesn't work
; (defmacro cast-to (cast-to x to-type) -> (%%cexp ( to-type -> to-type ) "%0" x))
(set! a_var (list:cons (cast {field=1 extra=2} ) a_var))
(set! a_var (list:cons (cast {field=2 bar=3} ) a_var))
(set! a_var (list:cons (cast {field=3 zed="fo"} ) a_var))
;; (set! a_var (list:cons (cast {error="error"} ) a_var))
;; ...there is only going to be one parametric instantiation
;; of this function, which is going to be called on any data
;; placed in the list
(define (with_list a_list)
(match a_list with
(list:nil) -> #u
(list:cons data next) ->
(begin
(printf (int data.field) "\n")
; (printf (int data.foofield) "\n") ;; error
(with_list next))
)
)
(with_list a_var)