Closed DavidMikeSimon closed 12 years ago
Argh, on further thought it occurs to me that this might simply not work, at least for function calls with no arguments. Consider:
foobar = fn()
ok
Under my proposed syntax, it's ambiguous if this is returning the atom ok
or calling a function ok/0
.
:-(
yes, it's ambiguous and with yecc can't be done since it generates lot of conflicts,
what I did added is the possibility to declare a function that takes no parameters omitting the parenthesis:
repeat = fn(N, Proc)
if N <= 0
ok
else
Proc()
repeat(N - 1, Proc)
@public
run = fn
repeat(5, fn
io.format("Intruder Detected! ~n")
)
Cool, thank you for looking into this and making that change, little conveniences add up. :-)
Please allow me to suggest an alternate approach for doing blocks parameters outside parens:
repeat(5) <- fn
io.format("Intruder Detected!~n")
This is very similar to the ->
arrow expression syntax; where ->
supplies the prior expression to the following function call as the first argument, <-
instead supplies the following expression to the prior function call as the last argument.
Er, on further thought, it might need to be a different operator than <-
, since it could be ambiguous in expressions like this one:
somefunc()<-3
Does that compute if the return value of somefunc/0 is less than negative 3, or does it call somefunc(-3)? :-/
Maybe better would be -<
maybe some similar syntax like ruby blocks?
http://www.skorks.com/2009/09/using-ruby-blocks-and-rolling-your-own-iterators/ http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/
maybe something like (imagine lists.map takes the arguments in the reverse order)
lists.map([1,2,3]) fn (X)
X + 1
Yes, Ruby blocks were exactly what I was thinking of! :-)
I don't like how Ruby uses a special hidden block argument for such things, but their syntax (and the example syntax you provide) is perfect.
here you have :D
https://github.com/marianoguerra/efene/commit/05e48b96fd6b0940804e16122e87e69b5c1482f7
play with them and tell me if there is any problem
note that there is a special case in ifene, if you make a function call and in the next expression you return a fun all by itself then it will be appended as last argument to the last function call
@public
run = fn
repeat(5)
fn
io.format("hi there~n")
this is a corner case we should document
Cool, thank you for implementing this!
The corner case you mention may be a good reason to change the syntax to use an operator.
maybe I can change fn for do like ruby (that would add a new reserved word)
Sounds good to me.
I'm getting a problem compiling struct.ifn in a clean checkout; it seems to have a function named 'do', which won't work anymore.
renamed it to do_on_attr in the last commit, thanks
Cool, thanks.
I've found another issue. The following Ifene code fails to parse:
run = fn
L = lst.map([1,2,3]) do (X)
X*3
-> lst.map() do (X)
X+1
io.format("~w~n", [L])
init.stop()
And if I convert it to Efene code, then it outputs the wrong result, printing [3,6,9] instead of [4,7,10]:
@public
run = fn {
L = lst.map([1,2,3]) do (X) {
X*3
} -> lst.map() do (X) {
X+1
}
io.format("~w~n", [L])
init.stop()
}
about the ifn code, I could remove the new line before -> if it comes after a }
or more generically I could remove the newline before any arrow? an arrow on itself on a new line should always be related to the previous line I guess. you seem to be good finding this corner cases, what do you think?
but it was hard for my brain to parse that too :)
about the fn code, it works for me
mariano@ganesha:~/tmp$ cat thing.fn
@public
run = fn {
L = lst.map( [1, 2, 3])do(X) {
X * 3
} ->lst.map()do(X) {
X + 1
}
io.format("~w~n", [L])
init.stop()
}
mariano@ganesha:~/tmp$ fnc -t erl thing.fn
-module(thing).
-export([run/0]).
run() ->
L = lst:map(lst:map([1, 2, 3], fun (X) -> X * 3 end),
fun (X) -> X + 1 end),
io:format("~w~n", [L]),
init:stop().
mariano@ganesha:~/tmp$ fnc thing.fn
Compiling thing.fn
mariano@ganesha:~/tmp$ fnc -r thing run
[4,7,10]
mariano@ganesha:~/tmp$
Hm, one nice general solution would be to remove any newline that immediately precedes a binary operator. That would allow me to use any operator I wanted, not just ->
but also:
X = some_function() do
foobar
+ some_other_function() do
borknarf
And also the simpler case of:
Y = some_long_expression
+ some_other_long_expression
However, this would be an problem to parse when using operators that are ambiguously binary or unary, like -
.
Maybe a better idea is to introduce a new syntax element that prevents meaningful interpretation of an adjacent newline? Sort of like the \
at the ends of lines in shell scripts, or the use of |
in Haml. I'd prefer something that goes at the start of lines, making the above examples:
X = some_function() do
foobar
| + some_other_function() do
borknarf
Y = some_long_expression
| + some_other_long_expression
By the way, you are correct that the .fn example works correctly. Today I learned that it helps a great deal if you actually recompile code before testing it again. :-)
just commited a fix for the arrow after a new line.
thanks for the report
PS: busy january :)
Cool, thank you. But, what did you think of my more general idea for having a syntax to ignore the previous CR?
On Tue, Jan 31, 2012 at 3:07 PM, Mariano Guerra < reply@reply.github.com
wrote:
just commited a fix for the arrow after a new line.
thanks for the report
PS: busy january :)
Reply to this email directly or view it on GitHub: https://github.com/marianoguerra/efene/issues/48#issuecomment-3746878
David Simon
On Tue, Jan 31, 2012 at 9:09 PM, David Simon reply@reply.github.com wrote:
Cool, thank you. But, what did you think of my more general idea for having a syntax to ignore the previous CR?
I like the idea, never used a language with it (I've seen some of them) I'm thinking on how many cases will make it useful and how it plays with ifene indentation..
I'm not sure about how to implement the interaction with ifene indentation, but conceptually, it should be consistent if the "|" makes the parser ignore the prior newline in terms of seeing it as a "statement end", but not skip the comparison of the current and prior lines' indentation to check for an implicit block close.
Useful cases would mostly binary operators that tend to get chained together, which are: addition, concatenation, the -> operator, and logical boolean operators. Though when it comes to addition and concatenation, there is a possible case to be made that it's more "functional-style" to instead list the stuff in an array and use sum or inject.
This one might be tricky to implement in the parser, but I'd be super pleased if Efene had it. It makes a lot of Ruby code so much more readable, particularly when writing domain-specific languages.
The basic idea is that this:
Should be equivalent to this:
This would only work at the statement level in Efene. In Ruby, you can nest paren-less function calls inside other paren-less function calls, although you shouldn't since it is confusing to read. But with Efene, that wouldn't even be possible, because Erlang and Efene syntax doesn't distinguish at the lexing level between a call to a function named x and a symbol/atom named x.
Besides making simple statement calls (particularly common ones like
io.format
) more readable, this syntax is especially nice because it allows you to create functions that look and act rather like language-level control structures. For example, I could write in Ifene a function to repeat an action X times:And use it like this: