REBOL3 tracker
  0.9.12 beta
Ticket #0002139 User: anonymous

Project:

Previous Next
rss
TypeWish Statussubmitted Date30-Mar-2014 13:08
Versionr3 master CategoryUnspecified Submitted byfork
PlatformAll Severityminor Prioritynormal

Summary Eliminate MAKE in favor of per-type construction functions
Description This is inspired by Cyphre's challenge as to whether an N x N construction matrix of MAKE is actually necessary, when the N x N matrix of TO is already confusing enough. It would at least effectively cut that confusion in half.

OBJECT vs. FUNCTION is a good example of where a different arity is enabled, and the help is available to be stylized per-type to explain the specifics... as well as per-type refinements. (For instance OBJECT/SAFE could replace CONSTRUCT, and /SAFE be documented in HELP OBJECT)

The current deviations between MAKE and TO are somewhat erratic. Note:

>> to object! [foo: 10]
** Script error: cannot MAKE/TO object! from: [foo: 10]
** Where: to
** Near: to object! [foo: 10]

Compare with:

>> probe to function! [[foo [integer!]] [print foo]]
make function! [[foo [integer!]][print foo]]

There'll still be that uphill battle to define the NxN matrix for TO, and a question of what the invariant of a TO conversion is.

(Note: Previously I have proposed an invariant of defining a "conversion" as something that doesn't "lose data" but is only allowed to "lose formatting"...which stabilizes after one step of format loss. So TO STRING! TO DATE! TO STRING! 12-12-2012 could return "12-Dec-2012" and that be okay, but TO STRING! TO DATE! TO STRING! TO DATE! TO STRING! 12-12-2012 couldn't return anything but "12-Dec-2012". That's defined in a separate ticket, but I do think there is some hope for that invariant and would appreciate voices of reason saying "yeah, if it did something besides that, it would be bonkers".)

Two potential concerns on axing MAKE that jump out:

(1) If the type is not statically known in your code, is there lost value in generality of code for construction where **MAKE type spec** offered more flexibility?

...it doesn't appear to me to be the case. And Cyphre's been using Rebol a lot longer, so if he doesn't think having MAKE as a generic constructor is a win, it might well not be. Construction doesn't sound like it has much hope for this "cross-type" invariant of (for instance) preserving data but not formatting.

(2) If the constructor types have the same name as the datatype (minus the exclamation point) then what about calling variables that name? Doesn't a lot of code have variables called BLOCK, for instance?

...well, I dunno. There's already a reluctance to call things "block" and instead "blk" or often "value". Certainly things aren't called "object" but rather "obj". The precedent seems to be stabilizing.

I think that **x: string 10** is more legible than **x: make string! 10**, and one could see disabling **x: string "foo"** and requiring using **x: copy "foo"** instead. I've been chided for the waste of a series and accident-prone-ness-of-forgetting a copy of writing **copy {}**, but I dislike being forced to encode a possibly meaningless length, but **string none** would satisfy me as a way of saying "make a new empty string and I have nothing to encode at the source site about my knowledge of the potential length--use a reasonable default." This "narrowing" of the construction seems it would generate clearer code, and with more discoverable help.

Cyphre's complaint that kicked this off was not liking the distinction between **make logic! 0** returning false and **to logic! 0** returning true. So he asked the broader question that I might sum as: "what's the real difference between a MAKE and a TO and what's the value in me having to care?" BrianH answered me when I asked the same thing as it being construction vs. conversion, but it seems construction can be narrowed fairly specifically.

I don't know about what I'd suggest about a constructor named **logic**. There's a lot less ambiguity in **value: 0** followed by **not zero? value** instead of a **logic value** that behaved with a different bias about zeros than **to logic! value**. I might suggest reserving the logic constructor and just having it throw an error and point to the help of "there is no logic construction function at this time, use zero? and not zero?". It is hard to imagine a constructor for a logic that might be able to provide insight about its usage... ("this is a logic value that I intend to have be TRUE about 95% more often than it is FALSE, adapt the implementation appropriately"). Maybe a quantum computer could act on that hint.

Anyway, being able to do:

>> help string
USAGE:
    STRING storage

DESCRIPTION:
    Construct a string with a specified preallocated buffer size
    STRING is a function value.

ARGUMENTS:
    storage -- Number of codepoints to preallocate space for, none to use default (integer! none!)

...just looks a heck of a lot better, more discoverable, and it does provide for future expansion with refinements or polymorphism of other specs. I think this could be a way to at least cut the NxN matrix problems in half.
Example code
;-- Obviously this has a lot of ramifications.
;-- But a sampling of current behavior:

>> make string! 10
== ""

>> empty? make string! none
** Script error: cannot MAKE/TO string! from: none
** Where: make
** Near: make string! none

>> probe to function! [[foo [integer!]] [print foo]]
make function! [[foo [integer!]][print foo]]

>> empty? object none
** Script error: object does not allow none! for its blk argument

>> make block! "abc"
== [abc]

>> make block! 10
== []


;-- New behavior:

>> string 10
== ""

>> empty? string none
== true

>> empty? object none
== true

>> probe to function! [[foo [integer!]] [print foo]]
** Script error: cannot convert TO function! from: [[foo [integer!]] [print foo]]
** Where: to
** Near: to function! [[foo [integer!]] [print foo]]

>> probe function/only [foo [integer!]] [print foo]
#[function! [foo [integer!]] [print foo]]

>> block "abc"
** Script error: block does not allow string! for its storage argument

>> block 10
== []


;-- R3/Backward could provide a MAKE with a case statement that called the per-type constructors.
;-- The theory, however, is that these would not be missed
;-- (Counterexamples of where they *would* be missed are solicited as comments here!)

>> make string! 10
** Script error: make has no value

Assigned ton/a Fixed in- Last Update28-Aug-2014 16:39


Comments
(0004389)
fork
30-Mar-2014 14:38

In writing this up and adapting it, I'm still trying to push my idea that the TO conversions for scalars don't do anything too tricky with blocks.

I think the safest thing for converting between blocks and scalars is to put the scalar in the block and to losslessly interconvert single element blocks to the scalar they contain.

The challenge to this has been "well how often do people really want to do that?"

>> x: to block! 12-Dec-2012
== [12-Dec-2012]

>> to date! x
== 12-Dec-2012

>> to date! [12-Dec-2012 "foobar"]
** Script error: You used a TO conversion on a block with a scalar target and the block contained something besides one solo value that would convert as that scalar alone. Did you have a sensible meaning implied by that? Well, who cares what you meant...because it's hard to define with an invariant! HostileFork doesn't like the unmanageable invariants in the NxN conversions matrix. So "I'm afraid I can't do that Dave", because he's always right about that kind of thing.

>> to date! ["12-Dec-2012"]
== 12-Dec-2012

:-P

But having TO be a little more invariant in that fashion, with nice and well documented constructors with refinements and type constraints like **date** would help a lot in easing any perceived "loss" by reigning in TO.

>> to block! "abc def"
== ["abc def"]

>> to string! ["abc def"]
== "abc def"

>> to string! ['abc 'def [1 +] 2]
** Script error: See that other stuff above.

I think that as we foray into new words like COMBINE, the idea that TO be all things to all people just seems unsustainable. If it had a few rules it could make sense as having an invariant. Then these construction functions could ease the pressure on TO.

Right now:

>> to date! [1 2 3]
== 1-Feb-0003

>> to string! [1 2 3]
== "123"

>> to paren! [1 2 3]
== (1 2 3)

And I think that it may not be seeing the forest for the trees to insist on this being the way such things are written, vs. having a cleaner invariant. How often do you have a combinatoric situation where a variable BLK being converted to a type TARGET can you sensibly say that the NxN primitive there has any value? The last one--a series to series conversion--is the only one I think should work. Compare the vast flexibility:

>> help date
USAGE:
    DATE spec

DESCRIPTION:
    Construct a date from constituent values
    DATE is a function value.

ARGUMENTS:
    spec -- The date dialect block of constituents, or UTC/Rebol formatted string (block! string!)

>> date [12 Dec 2012]
== 12-Dec-2012

>> date [month: 'dec day: 12 year: 2012]
== 12-Dec-2012

>> date "12-Dec-2012"

>> combine/with [1 2 (if false [3]) 4] {,}
== "1,2,4"

But this raises the issue that some things, like FUNCTION!, have a baseline constructor that *do not* "own" the name. Off the cuff I might suggest FUNCTION be recoded as a native, and then FUNCTION/ONLY be re-engineered as the non-copying and non-gathering variant. The concept that you have the full "raw" power to construct any type from that type's name minus the exclamation point seems like something people could bank on... and /ONLY could be the fallback if the common use case was too "abstracted" and "did too much" (which is how to explain /ONLY in general, as the great suppressor...)
(0004394)
fork
5-Apr-2014 22:08

People have expressed that the current treatment of nouns as a construction verb is not something they care for very much, and wouldn't want to extend. So changing these function names to things like MAKE-OBJECT and MAKE-BLOCK, while hiding MAKE from general use and perhaps only for mezzanine use, is a possibility.

Another possibility was the idea that maybe there could be a better use of the tilde than at present. For instance:

object: object~ [a: 10 b:20]
map: map~ [a b c d]
block: block~ 10

Or maybe before, to not conflict with existing applications:

object: ~object [a: 10 b:20]
map: ~map [a b c d]
block: ~block 10

It would be more succinct, looks all right, and wouldn't compete with variables who had the type as the name. If someone saw that you could just tell them "tilde means construct, type help on it" and get a pretty good answer for how to create things. Prefix-and, OR, etc. are probably used less frequently and could be given another name. bitwise-and? prefix-and? prand? (That last one is not my suggestion. :-/)

If anything, the tilde should probably be taken for "approximately equal".

But the general point that was that they probably are not used often enough to warrant taking a cool symbol like the tilde. This actually arose out of Rebmu wanting better use of tilde.
(0004395)
rebolek
5-Apr-2014 22:21

That makes no sense. Switching from MAKE STRING! to MAKE-STRING and hiding MAKE is basically same as TO-STRING and similar functions (it's there, but TO STRING! is faster). Adding tilde is bit better than MAKE-SOMETHING but it is function that will call MAKE, so why not let people use MAKE if they want?
(0004396)
abolka
5-Apr-2014 22:30

One advantage of `make-string` over `make string!` is that we can easily attach type-specific documentation to the former, but not the latter. (Similar for `to-` vs `to !`.) Readily available documentation makes a lot of sense.

However, the availability of `make-string` and similar does not really necessitate the removal of the `make` primitive. So similar with what we have with `to` at the moment, both `make string!` and `make-string` could continue to be supported.
(0004467)
BrianH
22-Jun-2014 07:31

Some types already have these, but the naming convention for these is not MAKE-OBJECT for the object! type, instead the function is named OBJECT. The function!, closure!, command!, native!, action!, op!, module!, object! and map! types have these constructor functions, though the OBJECT function needs a bit of work (#2118). Notable exceptions are none! and unset!, but neither of those need constructor functions.

It would make sense to keep with the existing naming convention for future types that would benefit from constructor functions. Don't start a new, more verbose, worse-looking convention.
(0004500)
Ladislav
28-Aug-2014 16:39

I disagree with eliminating MAKE. Also, I do not use the TO-* functions.

Date User Field Action Change
28-Aug-2014 16:39 Ladislav Comment : 0004500 Added -
22-Jun-2014 07:31 BrianH Comment : 0004467 Added -
6-Apr-2014 03:55 Fork Comment : 0004394 Modified -
5-Apr-2014 22:42 abolka Comment : 0004396 Modified -
5-Apr-2014 22:30 abolka Comment : 0004396 Modified -
5-Apr-2014 22:30 abolka Comment : 0004396 Modified -
5-Apr-2014 22:30 abolka Comment : 0004396 Added -
5-Apr-2014 22:21 rebolek Comment : 0004395 Added -
5-Apr-2014 22:08 Fork Comment : 0004394 Added -
30-Mar-2014 15:08 fork Code Modified -
30-Mar-2014 14:53 fork Comment : 0004389 Modified -
30-Mar-2014 14:51 fork Comment : 0004389 Modified -
30-Mar-2014 14:48 fork Description Modified -
30-Mar-2014 14:40 fork Code Modified -
30-Mar-2014 14:40 fork Code Modified -
30-Mar-2014 14:39 fork Code Modified -
30-Mar-2014 14:38 fork Comment : 0004389 Added -
30-Mar-2014 14:10 fork Code Modified -
30-Mar-2014 13:55 fork Code Modified -
30-Mar-2014 13:54 fork Code Modified -
30-Mar-2014 13:52 fork Description Modified -
30-Mar-2014 13:52 fork Code Modified -
30-Mar-2014 13:52 fork Description Modified -
30-Mar-2014 13:48 fork Code Modified -
30-Mar-2014 13:48 fork Code Modified -
30-Mar-2014 13:46 fork Code Modified -
30-Mar-2014 13:42 fork Description Modified -
30-Mar-2014 13:41 fork Description Modified -
30-Mar-2014 13:09 fork Code Modified -
30-Mar-2014 13:09 fork Description Modified -
30-Mar-2014 13:08 fork Ticket Added -