From a Haskeller’s perspective, this is the obvious solution. 'a
could be &[quoted word]
, and spread [1 2 3]
could be &[isotope group]
(or even &[isotope group integer]
), and so on. The elements of these series could simply be ordinary words, left unevaluated and unbound: one could test 'isotope = first type of spread [a b c]
, and so on.
But I’m not convinced a Haskell-like type system is a great fit for Rebol. The biggest issue is that we want to have union and intersection types, and there’s no easy way to integrate them into a system like this. One could possibly make it more ergonomic using type synonyms, but then you’d have to handle those as well when testing types, and it becomes more complicated than it should.
I much prefer your alternative suggestion of making constraint functions fundamental… but with some small changes. If type of ''a
is "ed?
, then I feel that type of spread [a b]
should be &isotope?
. But then one could have other basic predicates too. I think it’s particularly important to have a set of types &any-word?
, &any-block?
, and so on, which would match ‘under’ isotopes and quotes (and other sigils). From these, it should be possible to create other types by combining the existing predicates: for instance, a splice would be a type which is both &isotope?
and &any-group?
.
This does leave me uncertain about precisely how those combinations should be accomplished. The best idea I can come up with is to allow constraints to take arguments, like so:
>> splice!: &all [&isotope? &any-group?]
== &all [&isotope? &any-group?]
; or equivalently:
>> splice!: &all [isotope! any-group!]
== &all [&isotope? &any-group?]
>> match splice! spread [1 2 3]
== ~true~
>> match splice! [1 2 3]
== ~false~
; another demonstration, with more combinators:
>> series!: &all [
¬ isotope!
&any [any-block! any-group! any-path! any-tuple! string!]
]
== &all [¬ &isotope? &any [&any-block? &any-group? &any-path? &any-tuple? &any-string?]]
>> match series! "foobar"
== ~true~
>> match series! '[a b c]:
== ~true~
>> match series! spread [1 2 3]
== ~false~
I’m not sure how feasible this is to implement, though. As I recall, the key innovation which allowed constraints-as-types was implementing them as intrinsics. Is there some way of getting these combinators &all
/&any
/¬
to construct new intrinsics at runtime? I really don’t know. Possibly, these type constraints may need to have their own internal representations, different to that of ordinary Rebol functions.