MAKE Should Be Using *Dialected Constructors*

In thinking more about "what is MAKE", I feel like it is fundamentally a constructor.


When you think about Rebol's value proposition vs. other languages, what's the key idea?

DIALECTS. The freeform Jazz of programming. :saxophone:


So what should be the differentiating factor about constructors in Rebol vs. other languages?

CONSTRUCTOR DIALECTS. :saxophone: :guitar: :drum: :musical_keyboard:


When I say make my-type! [...] then what goes in that BLOCK! needs to be delegated to MY-TYPE! in some way to allow it the freedom to interpret the hell out of that block.

And it shouldn't only be allowed to be a BLOCK! full of madness. Consistent with history where you can write things like (make block! 10) to preallocate a size, you should be able to pass anything you want.

make my-type! 'some-word

make my-type! /some-refinement

make my-type! ${...}  ; gimme a bound FENCE!

make my-type! @[...]  ; special meaning if "inert" block received?

History Has Ridiculously Underformed The Potential

When you look at the TO/MAKE Matrix, it's full of meaningless choices:

rebol2>> make block! [a b c]
== [a b c]

Huh? What the hell use is that. Is it a copy? Is it a deep copy? Where's the beef?

Why isn't the constructor for block a literate landscape, subsuming the functionality of ARRAY (a terrible name for a verb) and much more?

>> make block! [4/8 initial: '*]
== [* * * *]  ; length 4, capacity 8, initialize elements with *

>> make block! /8
== []  ; just capacity 8

>> make block! 4
== [~ ~ ~ ~]  ; default to TRASH

There's an answer here that "dialect design is hard", and it's scary to put a dialect in the core. But there's decades of experience at this point. And maybe even ways for people to do their own rewrites and transformations so they don't have to use a make-block function but can customize make block! itself.


Ren-C Has Opened Up The Skies, and Will Do More

I was musing on the behavior of FENCE! in the interpreter and thought, what if it wasn't narrowly defined as being OBJECT! related. What if it was MAKE-related, with an object-generating default?

>> {x: 10, y: 20}
== #[object! x: 10 y: 20]

>> {[block!] 4/8 initial: '*}  ; maybe leading block means MAKE that?
== [* * * *]

>> {block! {4/8 initial: '*}}  ; maybe nesting FENCE! does it?
== [* * * *]

>> {block! 4/8 initial: '*}  ; maybe starting with WORD! is enough?
== [* * * *]

>> {group!}  ; synonym for -> copy '() ?
== ()

It's a half-baked thought, and I don't know what the ideal syntax is. But I feel like this could make FENCE! so much more.


So Coming Soon: The Great MAKE Purge.

In the combinatoric TO/MAKE matrix, there is a ton of garbage.

I've previously suggested that TO should not do any evaluation (e.g. be binding agnostic on what it gets in). I think that remains a good idea, that it be purely mechanical. I had some other ideas that are lingering.

I think it's time to further cull the "MAKE for the sake of thinking everything you pass needs to do something, even if it's trivial." I've been wiping out some of the garbage, but more is needed.

And we have to find a way to give users the ability to hook MAKE on their objects... such that something like make point-3D! [...] isn't locked into a trivial extension syntax of SET-WORD! and value, but truly able to do interesting things.

1 Like

Something worth pointing out here is that this gives you an elegance ARRAY can't do, because you have the type.

>> {[group!] 4/8 initial: '*}
== (* * * *)

>> {[the-fence!] 4 initial: '* /8}
== @{* * * *}

>> {[set-group!] 4}
== (~ ~ ~ ~):

This is pretty compelling.

ARRAY would need to be passed an additional parameter--either unconditionally or as a refinement.

I'll point out that I don't like the idea of something like:

>> {set-group! | 4}
== (~ ~ ~ ~):

Because FENCE!'s behavior would be built into the evaluator, and it's not nice to assume meaning for a WORD! like |. Better to cue off positions and "nonverbal" cues (no WORD!)

>> {[set-group!] 4}
== (~ ~ ~ ~):

This is pretty compelling.

Perhaps the most Rebol-y thing to do is to let you pick.

If you think it's clear enough to leave the block off, do so.

>> {fence! 4}
== {~ ~ ~ ~}

>> group: {group! /7}
== () 

>> capacity of group
== 8  ; actual pool size may be more than you asked

We do have the colon SIGIL! now, but that's pretty close to fence!: 4. We could say that :: in this context doesn't mean APPLY...

>> {fence! : 4}
== {~ ~ ~ ~}

>> {fence! :: 4}
== {~ ~ ~ ~}

As it so happens, // is going to be APPLY now due to slash's intimate tie to function execution.

So :: may be a reasonable candidate here, but I feel it needs to be optional.

In terms of strange musings on the power of FENCE!, I had an idea that it might be used to cast INTEGER! to DECIMAL!

>> number: 1020

>> {. number}
== 1020.0

Though here is where we get into keyword territory, because . would be redefinable (and it's looking like it will often be defined as a "this" or "self", to go along with .member acting as member selection).

So you couldn't do .: &[block] and get {. 4} to act like {block! 4}, you'd get 4.0

I'm wary of building in things like this, but we do have them built in here and there.

or maybe it shouldn't be inside the FENCE! at all...

>> block!:{4/8 initial: '*}
== [* * * *]

The idea of having an inert form that you can pass to MAKE later is interesting as well.

>> spec: @block!:{4/8 initial: '*}

>> make spec
== [* * * *]
1 Like