Idea for UCL: Methods
I’m toying with the idea of adding methods to UCL. This will be similar to the methods that exist in Lua, in that they’re essentially functions that pass in the receiver as the first argument, although methods would only be definable by the native layer for the first version.
Much like Lua though, methods would be invokable using the :
“pair” operator.
strs:to-upper "Hello"
--> HELLO
The idea is to make some of these methods on the types themselves, allowing their use on literals and the result of pipelines, as well as variables:
set h "Hello"
$hello:to-upper
--> HELLO
"Hello":to-upper
--> HELLO
(cat "Hello " "world"):to-upper
--> HELLO WORLD
The use of methods would be purely conveience. One could conceive of a type, like a CSV table, where there’s a need to perform a series of operations over it.
The potential downside of using :
is that it may make defining dictionaries more ambiguous. When the parser sees something that could either be a list or a dictionary, it does a scan to search for any pairs that may exist. If so, it treats the literal as a dictionary. But would this work with using :
as the method separator?
["Hello":to-upper]
--> Is this a dictionary {"Hello":"to-upper"}, or the list ["HELLO"]?
So that would require something. Might be that I need to require method invocations to occur within parenthesis for list literals.
Ambiguities like this aside, I’m planning to keep it simple for now. Methods will not be user definable within UCL out of the gate, but would be effectively another interface available to native types. For the builtin ones that exist now, it’ll most likely be little more than syntactic sugar over the standard library functions:
$hello:to-upper
# equivalent to
strs:to-upper $hello
Speaking of the standard library, the use of :
as a module-function separator may need some changes. A the moment it’s a bit of a hack: when a module is loaded, any procs defined within that module is stored in the environment with the operator: strs:to-upper
for example. The parser is sophisticated enough to recognised :
as a token, but when it parses two or more identifiers separated with :
, it just joins them up into a single identifier.
What’s needed is something else, maybe using something based on methods. The current idea is to define modules as being some special form of object, where the “methods” are simply the names of the exported symbol.
I was curious to know whether the language was capable of doing something similar now. It’s conceivable that a similar concept could be drafted with procs returning dictionaries of procs, effectively acting as a namespace for a collection of functions. So a bit of time in the playground resulted in this experiment:
proc fns {
return [
upper: (proc { |x| strs:to-upper $x })
lower: (proc { |x| strs:to-lower $x })
]
}
(fns).upper "Hello"
--> HELLO
(fns).lower "Hello"
--> hello
A good first start. This is theoretically possible in the language that exists at the moment. It’s not perfect thought. For one thing, the call to fns
needs to be enclosed in parenthesis in order to invoke it. Leaving them out results in an error:
fns.upper "Hello"
--> error: command is not invokable
The same is true when using variables instead of procs. I tried this experiment again using variables and it came to the same limitations:
set fns [
upper: (proc { |x| strs:to-upper $x })
lower: (proc { |x| strs:to-lower $x })
]
($fns).upper "Hello"
--> HELLO
$fns.upper "Hello"
--> error: command is not invokable
Obviously the parser needs to be changed to add additional support for the :
operator, but I also need to fix how strong .
binds to values too. But I think this may have legs.