TypL
TypL copied to clipboard
Why were tag functions chosen for the API?
TypL looks extremely interesting! Thank you for putting so much work into it already!
I have kind of a surface level question: why were tag functions chosen for the API? I'm sure there are good reasons but from the outside-in perspective, it seems that normal function calls would be a little more ergonomic since there's a good bit less syntax:
Using tag functions:
function isEven(v = number) {
return bool`${ v % 2 == 0 }`;
}
var x = number`42`;
var kind = bool`${ isEven(x) }`;
vs
Using "regular" functions:
function isEven(v = number) {
return bool( v % 2 == 0);
}
var x = number(42);
var kind = bool( isEven(x) );
Thanks!
TypL uses custom type syntax, which I did not want be invalid JS syntax. As such, it has to embed this custom type syntax inside `back-tick delimited template literals`
. The purpose of the tag function attached to the template literal, as opposed to just functions with the template literal passed in, is because it saves two extra characters (the surrounding ( .. )
parens).
As an example of the custom syntax:
function doSomething(vals = array`< int[], str[] >`) {
// ..
}
The < int[], str[] >
type syntax is a tuple (2-element array) where the first element is an sub-array of integers, and the second element is sub-array of strings. That custom syntax is buried in a template string so as to not invalidate the overall JS syntax itself. That string is tagged with the array
tag function, because that function knows to parse the type syntax in the template string, creating a validating function such that, at both compile-time and run-time, enforces that the vals
argument must to match the shape of < int[], str[] >
.
Ah, ok. There's a little more to this than I gleaned from the documentation.
Your explanation makes sense. For primitive data types though, is it possible to use the the "regular" function syntax? At least as an alternative API?
So both of these would be valid:
var kind = bool`${ isEven(x) }`;
and
var kind = bool( isEven(x) );
Maybe TypL can handle transforming it under the hood? A naive implementation:
function bool (value) {
var valueAsTypeSyntax = typeof value === 'string' ? value : `${value}`;
}
is it possible to use the the "regular" function syntax?
In theory, I don't see why TypL absolutely couldn't support a simpler function-only syntax for primitives. And I can see why, in certain cases, it might look nicer in usage.
However, I'm not convinced that's a good idea. Perhaps it is, I'm not rejecting it outright, but my strong inclination at first is skepticism.
For posterity, let me list a few initial thoughts/concerns about the idea:
-
I think it's important that TypL be pretty obvious and recognizable, from a readability perspective. I want it to look like something that stands out as a type annotation, rather than something that blends in as if it's just app code. I worry that
bool( .. )
will just blend in, whereasbool`${ .. }`
(though it's clearly a bit more unwieldy syntax wise) pretty intentionally stands out. -
By having two different approaches, it becomes less clear to those who are learning TypL which approach they should/must use in which circumstances. If for example you're using the function-oriented syntax for an annotation, and then realize you need to handle something more than a primitive, you then have to recognize the need to graduate to the other
`
-delimited syntax. I worry that overall, having this extra complexity will bring more mental overhead than the syntax shortcut provides, thus being a net-negative. -
Because of how tag functions are automatically called when tagging a template literal, your naive solution above won't actually work. A normal
bool(..)
function call and abool`tag function call`
have different signatures. It might be possible to overload thebool(..)
util so it can handle being called in both ways -- I've attempted things like that before, and indeed there is some limited version of that in a few places in TypL already -- but bottom line: that's really tricky to do, and may not be worth the effort and potential corner-case bugs.As such, I might have to provide different functions to be used for each call style. That would mean you might have to do
bool`whatever`
andbool2(whatever)
. That would suck, obviously. And unfortunately I don't think transpile-renaming would be sufficient to avoid that, since a key goal of TypL is to work correctly even if you skip the build-step and only hit run-time behaviors.IOW, this could get really tricky to implement... I'm not sure quite how we'd do it. It might be possible, but it also might just be too complex to be worth it.
So... those thoughts said, it's worth keeping the idea around to consider for the future. Not promising anything, but... at least we can see what questions would need to be answered to lift this to a viable feature.
Thanks for the detailed response!
I see what you mean. Thought No.2 was on my mind, I'm generally more in favor of One Way to Do It myself so I feel that any additional syntax does need a strong reason for existing. That said, I do feel that bool`${ .. }`
is a bit clunky. Not unworkable but I'd be a tiny bit worried about developer adoption if they view it as clunky.
Maybe an option that solves No.3 and maybe No.1:
var kind = bool`${ isEven(x) }`;
// is functionally the same as
var kind = bool.of( isEven(x) );
It's the same number of characters but feels a bit more conventional/less mysterious which I hope would be more attractive to developers and increase adoption. This doesn't really help No.2 aside from maybe being harder to get confused between these two options (than my original idea since there's larger difference between these two syntax options).
bool.of(..)
Heh, that's a reasonable option to consider for addressing the dual naming issue. I'd expect a lot of people to accidentally to do bool(..)
accidentally when they should have done bool.of(..)
... so I think the TypL compiler would have to identify the mistake and report an error.
Unfortunately, that means run-time usage could still fail. That's a downside for sure. But in balance it may not be as bad as the always-clunky-syntax.