Open ozra opened 8 years ago
I must disagree strongly with the statement:
A "soft-lambda" is part of the arguments, not an "add-on". That means it goes inside parentheses if they're used in a parenthesized call, and is separated with a comma if there are arguments before it. It still only goes last among the arguments.
I'm afraid that is a terrible idea, and a deal-breaker for me. Please reconsider.
What is meant by:
instance-vars do not need @ prefix at declaration site.
?
I also don't see what is wrong with @@
, or with .|.
I approve of many of the other changes listed.
@stugol:
type Foo
@x Int32
end
type Bar
x Int32
end
@@
-notation is way to close to the @
-notation for such a different use-case imo. Self.type-level-var
looks more appropriate imo, clearly showing that it is a globally scoped variable, scoped under current type.
Do you have some side-by-side code, to compare which looks clearer?What you approve of - I care little What you like and dislike - I make note of What constructive arguments you present - I value and enjoy
;-)
[regarding "soft lambdas"]
I'm afraid that is a terrible idea, and a deal-breaker for me. Please reconsider.
Why? Care to elaborate on the reason and give some examples? So far I've personally only experienced it as an improvement.
Self.type-level-var
is alright, but should not completely replace @@var
.
As to soft lambdas, in Ruby, I can do this:
[...].select { |n|
code
}.map { |n|
code
}
Granted, there are no parentheses in this example, but my point stands. To do the same with the blocks inside parentheses would be horrible:
[...].select({ |n|
code
}).map({ |n|
code
})
reduce
takes arguments and blocks.
Well, that would simply be like this in Onyx with the current syntax (parentheses are not required in calls, the soft-lambda simply go inside iff you use paren call):
-- no parens used:
list.select (n) ~>
code
.map (n) ~>
code
-- with parens:
list.select((n) ~>
code
).map((n) ~>
code
)
-- using auto parametrization:
list.select ~>
code
.map ~>
code
Regarding @@var
; why would that notation be good to keep in your opinion? Example?
Oh, and reduce:
list = [1, 2, 3]
reduced = list.reduce 0, ~> _1 + _2
-- or:
reduced = list.reduce 0, (acc, v) ~> acc + v
-- or:
reduced = list.reduce(0, ~> _1 + _2)
-- or:
reduced = list.reduce(0, (acc, v) ~> acc + v)
list.select (n) ~>
code
.map (n) ~>
code
Does Onyx cope well with lines starting with a .
? Ruby does not, for example. As soon as it finds a complete statement, it assumes that the statement is finished. Meaning that only the first two lines of your code would be parsed; and the third line would be an error.
Regarding @@var; why would that notation be good to keep in your opinion? Example?
Mainly because my editor highlights @@
-vars with a deeper orange than @
-vars. Whereas it would be unable to distinguish between class variables and class methods, and not highlight them at all.
Also, the @@
stands out, just like $
. You wouldn't replace $
with Global.
, would you? Because then it wouldn't be obvious at a glance that you were using a potentially dangerous global variable.
Does Onyx cope well with lines starting with a .
Well, fairly, I'm sure there's room for improvement, after all everything's very new and alpha atm. Just tested this which works fine:
x = list
.select (n) ~>
code
.map (n) ~>
code
Regarding @@ var...
You're right damn it! There must be a distinction between type level vars and funcs, otherwise one will always get precedence in name clashes (must verify this!).
I'll throw in another idea: Type@type-level-var
, Type.type-level-func
I've had a thought. In Ruby and Crystal, you can't access class-level variables (or instance variables) from outside the class. So Type@var
or Type.var
make no sense whatsoever. Just use @@
, and take the others out.
Gotchas For Crystalers and Rubyists
It's reasonable that some of you who find interest in Onyx know a bit of Crystal.
There are some differences that can be confusing, likewise if you come from a Ruby background.
end
keywords are optional, but significant when used, and can also be more explicit:end-type
-- comment here
Symbol
s are calledTag
s and are written#symbol_name
, not:symbol_name
(think hash-tag)%"a"
not'a'
- they're not too common except in special code bases.::
for namespacing / module-hierarchy-digging, simply use dot:MyMod.IsMuch.More.Natural.ToUse.like-this()
thanMyMod::IsMuch::More::Natural::ToUse.like-this()
foo SomeType = 47 -- foo typed to SomeType and assigned 47
module
is a module, a "type-partial" is declaredtrait MyTrait
, notmodule MyTrait
, and mixed in to a type withmixin MyTrait
- notinclude
do
is a generic "nesting starts" keyword, not a "crystal/ruby-block" starter.do
,then
,:
,=>
are exactly equivalent. Example:if foo then "ok"
,if foo => "ok"
,if foo: "ok"
, etc.(params, here) ~>
or~>
only. Instead of&.do_stuff
, you would use the visually similar~.do_stuff
(to keep with the style of the other soft-lambda notations.)_x
where x is the position in parameters it should represent. Count begins with 1.This means that in Onyx you could write (a few variations here, result unused):
Bitwise operations are not commonplace and therefore they have been demoted to the Haskellish form:- this will change back in a push not to soon....|.
,.&.
,.^.
,.~.
- that is, simply surrounded with dots (think bits...). Note that the same goes for any type overloading those operators: List "unique join" would also be[1, 2] .|. [2, 3]
.Type(Type2(Type3))
, in Onyx you can currently use<
/>
and[
/]
, like so:Type[Type2<Type3>]
.foo = Foo("Param to init")
which is the same as calling.new
. Parentheses has to be used if there are no arguments:foo = Foo()
, otherwise it just represents the type (unlike first-hand callables that are calls primarily and values only when specified). Another example with generics:list = List[String]()
. The short form is preferred instead of calling new.foo = (x I32, y I32) -> x + y
my-lambda = (x Str) -> say "x: {x}"; end; my-lambda "47"
"{interpolate-me}"
, not"#{...}"
and
,or
,is
,isnt
,not
is available in addition to&&
,||
,==
,!=
,!
- they behave exactly the same as their symbolic counterparts, it's a mere matter of lexical choice.for
-loops are available - they're pure sugar for.each*
-iteration, and so scoping and behaviour exactly mimics method based soft-lambda iteration. A multitude of syntactical variations are currently available, whatever you find natural to use will likely work. The abundance of choices will be reduced by consensus.initializer
is simply calledinit
self
does not refer to the current type, you useSelf
orType
(capitalized). One of the terms may be ditched.Type.my_method(params) ->
(orSelf
...) and are currently referred to as Type Level Functions.@@class–var
orType.class–var
. These also, are called Type Level Variables. I'm inclined to ditch the@@
-notation.@
prefix at declaration site.StdInt
(platform width), not Int32 by default.if
. Or, better phrased:if
has an alternative nest-start/else notation:foo = if bar is 47 ? 1 : 2
. You could as well writefoo = if bar is 47 then 1 else 2
case
constructs currently, try them out and don't get stuck with what you're currently used to - you might find something you like more. You can currently write them exactly as in Crystal. The huge amount of styles now available will be reduced based on RFC input.def
orfn
, however the idiomatic way is justmy–def(params) -> body
. It's uncertain whether the keyword style will remain....par–name
- not*par–name
my-evil-func(x) ->! x.bar = 47 -- returns nil
begin
/rescue
/else
/ensure
constructs are namedtry
/rescue
/fulfil
/ensure
.fulfil
is the same aselse
in Crystal but can be used even when there's no begin/rescue defined - to ensure that something always run on successful return of a function, but not if it raises and is caught by some other spot up the call chain.'link("some_lib")
. The names differ from Crystal too. See issue on pragmas.Probably some more things - help out by commenting on what confuses you!