Open ozra opened 8 years ago
I think the operator style is much preferable.
Perhaps impl?
Yes, impl?
is simple, and reasonable, given the mindshare such re-occuring language constructs gets.
Furthermore: of?
operator should just be of
.
The literals syntax type declaration style [] of SomeType
should likely be changed.
I disagree. x as T
means "cast x to type T", while x of? T
is a predicate, and therefore a question. The operator should be of?
, leaving of
free to perform its current task.
As a corollary to this, I propose that in
be replaced with in?
.
Yes, the in
is what got me thinking the opposite ;-)
-- reads well
if some-thing in other-thing then do-stuff
-- reads awkwardly
if some-thing in? other-thing then do-stuff
But - on the other hand - it makes the "keyword operator" pop out much more clearly (of course syntax highlighting helps, but as I often state: if the language looks clear even in black-white - that's awesome) - which very well might outweigh any other argument. So, I think I'll re-position my opinion on that.
I want to expand on the argument for "hard" language operator keywords rather than methods. Which means allowing method-call style as alternative might not be in.
Since they are recognized as special constructs (of a operator'ish syntactic nature) they can have a tighter operator precedence than ||
, &&
, etc. This means that less parentheses are required to obtain the intended behaviour in conditions, and less confusion for "methods" that aren't really methods and might behave slightly differently in parsing: confusing, surprising deviations with syntax falsely implying it is a common user-definable construct.
if foo of? MysticType and foo in? allowed-vals then do-stuff foo
Which natural precedence would be:
if ( (foo of? MysticType) and (foo in? allowed-vals) ) then do-stuff(foo)
Compare:
if foo.of?(MysticType) and allowed-vals.includes? foo then do-stuff foo
Since an "honestly" parsed method-call syntax otherwise would give: foo.of?(MysticType && allowed-vals.includes?(foo))
- which of course blows to bits.
I still like the idea of user-defined operators.... ;)
PR! ;-)
But there's pretty much zero chance of my understanding the code enough to implement such :(
Regarding the issue at hand, I had an idea during the day while chasing the bulls - (Yes! Three bulls ran away from the field this afternoon under my watch (damn!). They've been all over the fucking neighbourhood, all the way down to the lake - we just finally succeeded to lure them in to the barn minutes ago, yikes. X-/ ) - well. Phew. Had to get that stress weight out. Eh, back to train of thought: Interfaces.
Onyx has Traits. Those are nominal. Interfaces are structural. I figure like this: one simply use another way of testing capabilities, witch matches structurally against modules, traits or types, rather than nominally.
Normally you'd go if foo of? SomeTypeOrTrait do foo.stuff
- which checks for relation nominally.
But, then, thinking about it: implements?
. That's the exact thing that should check structurally!
if foo implements? SomeTypeOrTrait do foo.stuff
of?
compares against super-types, modules and traits - nominally - "is foo made up of that type/trait in any way?"
implements?
should of course reasonably just check _"are the things in that type/trait implemented to full match within foo?"_, whether one is passing just a method signature or a whole Trait. In which case all it's defs should be checked to see that corresponding signatures are implemented in foo.
It's a matter of figuring out if it's plausible to do a sufficiently efficient routine so that it doesn't compile to slowly and that also generates fast-as-f**k final code.
It's an idea!
So, you mean:
is_subclass_of_T = thing impl? T -- i.e. is_a?
has_trait = thing impl? MyTrait
has_method = thing impl? to-string -- i.e. respond_to?
has_specific_method = thing impl? to-string(Int)
Where thing
is an instance or a type.
I think I was too exhausted still when I came in and wrote that, I'll clarify:
is_subclass_of_T = it of? T -- "is_a?" - it's a derivation _of_ T
has_trait = it of? MyTrait -- "is_a?" - it's made up also _of_ MyTrait
has_method = it impl? to-string -- "respond_to?" - it _implements_ the method
-- in contrast to deriving it _of_ some T
has_specific_method = it impl? to-string(Int) -- same here, but more specific
has_all_methods = it impl? MyTrait -- it _implements_ all the methods with the
-- specific signatures that are defined in
-- MyTrait - in constrast to having derived
-- the methods _of_ MyTrait
Where it
is an instance. For types, constraint declarations would be
awesomeness (both in arg restrictions and for generic constraints).
That, however, really need to be part of Crystal core in order to not break with the precious module universe. I'm trying to get the superficial things of Onyx in order faster so that I can help out more in progressing the actual core used in Crystal and, by derivation, Onyx. There's a lot of pressure on asterite, waj and the others, it would be nice to be able to help out in furthering the quality of Crystal, along with Onyx.
If x of? MyTrait is true
then it will always be true that x impl? MyTrait is true
. But not necessarily the other way around.
Let's clarify it in code:
trait MyTrait
foo(x Int) -> Int: x + 1
bar() -> "hey"
end
type Foo
mixin MyTrait
end
type Bar
@my-str = "yeay"
foo(x Int) -> 1 + x
bar() -> Str: @my-str
end
foo = Foo()
bar = Bar()
foo of? MyTrait --> true: it has derived the "genes" of MyTrait
foo impl? MyTrait --> true: it implements all the methods in MyTrait (duh!)
bar of? MyTrait --> false: it isn't a "relative" of MyTrait - no "transfer"
bar impl? MyTrait --> true: fully implements the definitions in MyTrait _seen_
-- as an "interface" (even though it _may_ have
-- implementations of it's own [like in this case] - unlike
-- interfaces). MyTrait could however have been entirely
-- abstract, and thus pose _even more_ as an interface.
Was that more clear?
Links the issue, while I'm at it: #72
What's the point of of?
then? We have is_a?
for inheritance tests; and impl?
for structure...
of?
is Onyx naming for is_a?
- a naming I find to be more exact in meaning, more terse, and better looking.
"X" can't be a SuperType, TraitA and TraitB - however it can be made of all of those.
Regarding getting the declaration type vs the current type at run-time, which I didn't list above, I think these are reasonable telling choices (don't know about level of abbreviation / verbosity):
-- ~~the-type = declaration-type some-var~~ - too long
the-type = decl-type some-var
the-type = d-type some-var
curr-type = some-var.acting-type
curr-type = some-var.act-type
curr-type = some-var.a-type
I will remove the other variations (r-type, etc.) and make all the above available, to figure out which verbosity level gets most use in actual code.
Regarding the others - both method-style and operator-style are allowed for a time of trial. And both in
and in?
Well. One more thought on variation for the get-types:
t = dtype-of some-var
t = some-var.atype-of
-- more verbose:
t = decl-type-of some-var
t = some-var.acting-type-of
-- or fucking simply:
t = dtypeof some-var
t = some-var.atypeof
Closer reflecting crystals typeof
. With the -of
, it's obvious the terse one-letter prefixes are preferable.
I'll just mention that in my local repo (not pushable atm) you can also use isnt
and not
(and redundantly is
) in _operator position. Which works nicely with above:
isnt-some-type = x isnt of SomeType
-- vs:
isnt-some-type = not (x of SomeType)
ok-value = some-val isnt in forbidden-values
I think it's a great way of expressing negations with the operators!
[ed: it might be pushed since months back, but I don't think so]
How about some-val not in forbidden-values
?
Just checked the source — that's implemented too. :)
Was touched upon in https://github.com/ozra/onyx-lang/issues/74#issuecomment-212302086, and some places else, but I think it needs to be issued properly here.
I favour the following, except
implements?
which is too long imo. Whether they should be possible to express as both "calls"/"operator" and also as "method-calls", I am not sure (the latter being favoured in Crystal):And of course, there's the question of signatures or name only in
implements?
(see https://github.com/crystal-lang/crystal/issues/2549#issue-152756236)