Non-Words in Function Parameter Spec Dialect

Historical Redbol only had WORD!s in the BLOCK! that came after a parameter:

foo: func [
    arg [block! word! number!]  ;  all words in block
] ...

But when Ren-C introduced the paradigm-breaking NULL that could not be put in arrays, that meant there was no null! datatype. To fill the gap, the tag <opt> was chosen to indicate the parameter as optional--hence possibly null:

foo: func [
    arg [<opt> block! word! number!]  ;  now it's WORD!s and TAG!s
] ...

I liked using a TAG! for how it stood out (though in retrospect I'd have probably chosen <null>, but everything was named differently then). Other quirky ideas were floated, like being able to put a leading slash on the typeset block:

foo: func [
    arg /[block! word! number!]  ; like a refinement, but on the types
] ...

That didn't gain traction, and probably shouldn't have.

Then when early efforts faced another value state that couldn't be put in a block and didn't have a type, <void> came onto the scene...because as with there being no NULL!, there was no VOID! datatype.

Tag Modifiers Which Weren't Type Checkers Showed Up

The ability to take a parameter but get an immutable view of it was added as <const>.

Parameters that would accept being at the end of input and evaluate to null in that case were <end>

<variadic> and <skippable> came into existence.

These "parameter-control tags" seemed to me to be a distinct category from typecheckers like <opt> and <void>. Having them all use TAG! felt like too many tags.

So I mused about splitting the roles, something like:

[<const> #null type!]
-or-
[#const <null> type!]

But I didn't like the look of it enough to move on it. So things like [<const> <opt> type!] stuck around while I wondered about it.

Today, You Can Specify Any Type Check By Function

There's still no NULL! or VOID!. But with the way things work now, you can use functions as "type predicates" to recognize things that aren't datatypes in their own right:

foo: func [
    arg [null? block! word! number!]
] ...

What's good:

  • It leaves TAG! for the properties like <const> that don't have to do with type recognition... but rather controlling the parameter in a more special way.

What's bad:

  • It loses that kind of special look that tags gave to arguments that could take null. It blurs together, especially with things like LOGIC? and CHAR? for other non-fundamental datatypes (characters are just single-character issues now, and ~okay~ and ~null~ isotopes of WORD! implement logic)

For a time I tried using quasiforms to signify antiforms, and it wasn't too bad, seemed to work better with the tags:

foo: func [
    arg [~null~ block! word! number!]
] ...

foo: func [
    arg [~null~ <const> block! word! number!]
] ...

But eventually things like ~word!~ became the way to type test a "quasi-word", and so that was out.

What about return: <null> and return: <void> etc. ?

These two special uses of tag! with no block have been used in various places.

How necessary is it? Well, you either write things like:

comment: func [
    return: <void>
    discarded [any-value!]
][
    return ()
]

Or you have the contraction:

comment: lambda [
    []: <void>
    discarded [any-value!]
][
]

This style of "don't even worry about writing a RETURN" has the widest applicability to TRASH and VOID. We don't strictly need it, but I've gotten used to it.

:thinking:

Having gone through a few different iterations of this now, I think I've come to like the TAG! alternative options for types where you can say <null> instead of null?, it stands out a bit better for that important return type.

And I think that #const being a different part of speech where it's not a typecheck feels like it makes some amount of sense.

Though since we have a working const facility... I am wondering if we should just go ahead and bias it to say you get a const reference unless you say #mutable on things. Languages have really struggled with this tough question. I feel like marking any mutable arguments wouldn't be that much of a burden.

You'd need to be able to limit what things were mutable, like [[#mutable block! text!] [integer! tag!]] or something like that.

Anyway, Long Story Short, I like <null> and <void>

...and also <opt> and <opt-out>.

So I think they're going to stick around, and other things may get pushed out of the way because I feel TAG! is getting overloaded in the typespec dialect.

1 Like