4

I occasionally get conflicted when using a language that isn't Rebol (happens from time to time) and get to thinking, hm—would life be easier in Rebol if this thing worked this way?

To illustrate my current instance, in Rebol you send a message as follows:

send foo@bar.baz "A Message"

It's always been one of the killer one-liners, very concise, most elegant. Using refinements, you can start to build up a more complex message:

send/subject/attach foo@bar.baz "Hi Foo!" "A Message" %me.jpg

Still relatively concise, but starting to fill out a little. As you sculpt your message, it starts to get a bit messy:

send/subject/attach/header
    [foo@bar.baz eric@bar.baz]
    "Hi Folks! – Chris"
    "A Message"
    make object! [X-User-Agent: join "Rebol " system/version]

That's the current prescribed way to send an email (line feeds are optional here, but become essential as component parts start to add up).

Rebol is nothing if not versatile though, we can dream of an alternate approach using dialects:

send [
    ; no delimiters required, we can discern recipients
    ; by the use of the email! datatype:
    foo@bar.baz eric@bar.baz

    ; two string! values indicate the first as the subject
    ; second as the message
    "A Message"
    "Hi Folks!"

    ; a few attachments courtesy of the file! datatype
    %me.jpg %you.jpg %the-world.jpg

    ; and then some set-word!/value headers
    X-User-Agent: "Rebol"
]

Sans comments:

send [
    foo@bar.baz eric@bar.baz
    "A Message"
    "Hi Folks!"
    %me.jpg %you.jpg %the-world.jpg
    X-User-Agent: "Rebol"
]

Pretty spiffy, but dialects get a bit tricky as you start to pull in conditional arguments, external data, further customisation. We can build that up somewhat procedurally:

send collect [
    keep foo@bar.baz
    if "Monday" = pick system/locale/days now/weekday [
        keep eric@bar.baz
    ]
    keep subject ; 'subject defined elsewhere
    keep {A Long Message}
    keep read %photos/
    keep compose [X-User-Agent: (join "Rebol " system/version)]
]

Seems reasonable, except we lose a bit of expressivity as the implicit metadata offered by datatypes is obscured by the additional code. Even just the addition of words not of our current domain (collect, keep).

Which brings me to the object-based world (which seemingly covers the way the popular languages out there approach things). In this composite, non-existent Language X, we recover some of the expressivity through the explicit use of named actions:

message = mailer.new

// pity poor language X, only one string type:
message.addRecipient 'foo@bar.baz'
if (today = 'Monday') { // assumed 'today value 
    message.addRecipient 'eric@bar.baz'
}

message.subject = "A Message"
message.body = "Hi Folks!"
message.attach filesystem.read 'photos/'
message.header['X-User-Agent'] = 'Language X'

message.send

// Rebol's SEND function needs a return value with some
// status feedback. For another day...
if (message.isSentOk) {
    print "Hooray!"
}

Beauty is in the eye of the beholder: this approach leaves you in no doubt what each line is for, yet simultaneously (to my eyes) appears heavy-handed. We get to our destination procedurally, send happens after we've established the details of the message. In my moments of doubt, I wonder if having send [...] states the intent before we're ready.

You could easily transliterate the Language X approach to Rebol, or have it inform the approach taken by a dialect:

mailer [
    new message
    add recipient foo@bar.baz
    if today = "Monday" [
        add recipient eric@bar.baz
    ]
    subject: "A Message"
    body: "Hi Folks!"
    attach read %photos/
    send
    probe status
]

My question then is: Is the Language X object approach/influence something to covet, or does one or other Rebol approach retain enough expressivity as complexity increases?

PS: Not just mail—I think about this with regard to the <canvas> tag and the idea of a procedural graphic format as opposed to the explicit <svg>.

3
  • I don't know the first thing about Rebol, but in C# you can set method arguments by position, by name, or in an object initializer by property name. Named arguments can appear in any order. You can also have optional arguments with default values (optional, named arguments must appear at the end of the parameter list), reducing the number of constructor overloads that are required. The variations in style are carefully calibrated to cater to varying levels of complexity. If none of those tickle your fancy, you can try a fluent interface. Sep 22 '15 at 8:20
  • @RobertHarvey Can you sketch out how expressing send in C# might improve how the above examples read? I'm not familiar with C#, but would be curious how it differs from the common Language X approach. I'm not looking for specific working code, just an illustration of the intersection of language constructs and the domain specific elements.
    – rgchris
    Sep 22 '15 at 14:51
  • A short Rebol primer.
    – rgchris
    Sep 22 '15 at 15:03
1

The Language X object approach/influence makes the instruction context explicit for each instruction. Dialects can make that context implicit, not having to repeatedly reference "message" as in the language "X" example. In the object world, "fluent interfaces" (noted by Robert in his comment) have been become popular to achieve a similar result.

A Rebol dialect can be arbitrarily powerful so I don't see that the language X object approach is one to covet. However, the object approach should be used as a benchmark - "is my dialect adding value over a simple object interface?", "does my dialect express the intent more clearly and simply?", "does the benefit of the dialect outweigh it's implementation cost?". If the dialect does not add value is it really required, or is there a better purpose for a dialect?

The hard part of dialects and of fluent interfaces is designing the interface/grammar. Goals like working out what expressions are useful to the client code, providing enough flexibility, robustness in the face of future changes and retaining "enough expressivity as complexity increases" do not seem to be easy.

Rebols value types are easy for a dialect interpreter to recognise, the type becomes a single value expression - set this variable with this value, like you've shown in your declarative dialect. Using the value types this way is really about using shortcuts (syntatic sugar) for some expressions. As complexity increases I think the approaches you've shown may have a problem because they are mixing conceptual contexts. For example, conceptually, the recipient email addresses will be used for two concepts: SMTP and RFC822. The file references are shortcuts for MIME expressions. If the dialect continues to mix multiple conceptuals contexts within it's keywords/values maybe it will become overly complex losing expressivity as more complex expressions are tried. Perhaps a way forward is to think of how dialects from different conceptual contexts will integrate so that expressivity is not lost as complexity increases. Shortcuts can be retained, but delving into a specific dialect as needed for expressions specific to that concept. The dialect might be embedded within another as a block introduced by its name represented as a word. As an aside, a MIME dialect could be reused outside of emails, for web purposes.

So I guess I'm suggesting a third approach, and I admit one I have not properly explored, in which shortcuts are provided for the simplest easiest uses as you've done and for finer control for a specific context call a named embedded dialect.

2

In c#, it is

Send("foobar@baz","A Message");

or

Send("foobar@baz, eric@bar.baz", "Hi, folks","A Message", "me.jpg");

No need to disambiguate the method names, thanks to overloading.

With named parameters, it becomes

Send(addresses = "foobar@baz, eric@bar.baz", 
    title = "Hi, folks", 
    message = "A Message", 
    attachment = "me.jpg");

Any or all of which can be optional.

The "message" variation looks like this. It may seem heavy-handed, but each bit of code means something:

message = mailer.NewMessage();

message.addRecipient("foo@bar.baz");

if (DateTime.Now.DayOfWeek = DayOfWeek.Monday) { 
    message.AddRecipient("eric@bar.baz");
}

message.Subject = "A Message";
message.Body = "Hi Folks!";
message.Attach("photos"); // Folder within 'Current" folder
message.Header.Add("X-User-Agent", "Language X"); 

message.Send();

With object initializers, it looks like this. Note that you can use a factory method for the initial object creation and still take advantage of this syntax:

message = mailer.NewMessage()
{
    addresses = "foobar@baz, eric@bar.baz", 
    title = "Hi, folks", 
    message = "A Message", 
    attachment = "me.jpg"
};

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.