Heavily inspired by kotlin and groovy, what would the following syntax simplication enable?
a {}
// syntact sugar for
a(function() {});?
https://www.safaribooksonline.com/library/view/the-ruby-programming/9780596516178/ch08s08.html
after (100) {
// run just once after 100ms
}
every (100) {
// run forever every 100ms
}https://hugotunius.se/2014/08/19/custom-control-structures-in-swift.html
Swift has a feature called @autoclosure which wraps any expression passed in a closure.
func _while(condition: @autoclosure () -> BooleanType, action: () -> ()) {
while condition() {
action()
}
}
var i = 0
_while(i < 10) {
println("\(i)")
i += 1
}Good discussion on reddit.
continue and breakbreak can break out of any block. For example:
foo: { a = 1; break foo; a = 2 }
console.log(a) // 1Apparently, you need to be lexically nested to break to labels. For example:
foo: while(true) { console.log("hi"); break foo; }
console.log("left foo")
bar: while(true) { console.log("hey"); break foo; } // throws "undefined label foo"
console.log("left bar")Works with if:
foo: if (true) {
console.log("this gets executed");
break foo;
console.log("this does not");
}
console.log("this does too")Whether it is a modifier that gets used in the call site (e.g. for each(array) {}) or at the declaration site (e.g. ```loop function foreach() { ... }). For example, you used a modifier in the function call:
loop function a(expr, block) {
}
a(true) {
...
break;
...
continue;
...
}It gets desugared to:
__exit__: a() {
__block__: do {
...
// all "breaks" here are replaced for "break __exit__"
break __exit__;
...
// all "continues" here are replaced for "continue __block__"
continue __block__;
...
} while (false);
}For example:
loop function foreach(array, block) {
for (item of array) {
block.call(item);
}
}
foreach (item in [0, 1, 2, 3]) {
if (item < 2) {
continue;
} else {
break;
}
}For example:
// a label gets added automatically at the top of the statement
__exit__:
foreach (item in [0, 1, 2, 3]) {
// a labelled block gets added automatically wrapping the block param.
__block__: do {
if (item < 2) {
// continue leaves the __block__
continue __block__;
} else {
// arg-less break gets desugared into breaking to the head of the function.
break __exit__;
}
} while (false);
}What happens with nested block params? E.g.
foreach (let item in array) {
foreach (let other in item.array) {
// will break and continue do the right things here?
if (other.i == 1) {
break;
}
continue;
}
}continue and break can take a label as a parameter:
foo: while (true) {
console.log("this gets executed");
break foo;
console.log("this never gets executed");
}
console.log("this gets followed");foreach(let item in iterable) {
if (item == 0) {
break end;
} else {
continue foreach;
}
}
end:but if break and continue from "loops" such as arr.forEach are rare enough, why not use a real label for labeling the exit point?
Really great considetations here!
http://wirfs-brock.com/allen/files/jshistory/continue-break-lambda.pdf
This only works for statements (so, not allowed inside expressions), but ...
function unless(block) {
if (!expr) {
return block();
}
}
while (true) {
unless (false) {
break;
}
}Could desugar to:
while (true) {
{
let result = unless (false, function() {
return {break: true};
});
if (result.break) {
break;
} else if (result.continue) {
break;
} else if (result.return){
return result.value;
}
// otherwise, just carry on ...
}
}Here is how ruby deals with these challenges:
def foreach(list)
puts "Running foreach on #{list}"
# you can call the block using the yield keyword
for i in list
puts yield(i)
end
puts "End of method"
end
puts ""
puts ""
puts "Sample #1: basic usage of blocks"
puts ""
puts ""
foreach (0..2) {
# This is how you declare arguments to the block
|value|
puts "Hi from the block with args: #{value}"
'A return value!'
}
puts ""
puts ""
puts "Sample #2: breaks inside blocks"
puts ""
puts ""
# breaks just return from the block and return null.
result = foreach (2..3) {
puts "Hi from block with break!"
break
}
puts "Result is nil, right? #{result == nil}."
puts ""
puts ""
puts "Sample #3: breaks inside blocks with for-loops nested"
puts ""
puts ""
# For example, break inside a lambda doesn't leave the
# outer lambda:
for i in 0..2
puts "Iterating: #{i}!"
foreach (0..i) {
|value|
puts "Iterating #{value}"
# This only leaves the inner foreach, not the outer for
break
}
end
puts ""
puts ""
puts "Sample #4: what does continue do?"
puts ""
puts ""
# For example, break inside a lambda doesn't leave the
# outer lambda:
for i in 0..2
puts "Iterating: #{i}!"
foreach (0..i) {
|value|
puts "Iterating #{value}"
# This only leaves the inner foreach, not the outer for
next
}
end
puts ""
puts ""
puts "Sample #5: what does return do?"
puts ""
puts ""
def foo
foreach (0..0) {
return "hello world"
}
return "not this"
end
# Returns "hello world" rather than "not this".
foo()And console:
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
Sample #1: basic usage of blocks
Running foreach on 0..2
Hi from the block with args: 0
A return value!
Hi from the block with args: 1
A return value!
Hi from the block with args: 2
A return value!
End of method
Sample #2: breaks inside blocks
Running foreach on 2..3
Hi from block with break!
Result is nil, right? true.
Sample #3: breaks inside blocks with for-loops nested
Iterating: 0!
Running foreach on 0..0
Iterating 0
Iterating: 1!
Running foreach on 0..1
Iterating 0
Iterating: 2!
Running foreach on 0..2
Iterating 0
Sample #4: what does continue do?
Iterating: 0!
Running foreach on 0..0
Iterating 0
End of method
Iterating: 1!
Running foreach on 0..1
Iterating 0
Iterating 1
End of method
Iterating: 2!
Running foreach on 0..2
Iterating 0
Iterating 1
Iterating 2
End of method
Sample #5: what does return do?
Running foreach on 0..0
=> "hello world"
Another example in Ruby:
def iffy(condition)
if (condition) then
yield()
end
end
iffy (true) {
puts "This gets executed!"
}
iffy (false) {
puts "This does not"
}
for i in 0..1
puts "Running: #{i}"
iffy (i == 0) {
# This does not break from the outer loop!
# Prints
#
# Running: 0
# Running: 1
break
}
end
for i in 0..1
iffy (i == 0) {
# This does not continue from the outer loop!
# Prints
#
# Running: 0
# Running: 1
next
}
puts "Running: #{i}"
end
def foo()
iffy (false) {
return "never executed"
}
iffy (true) {
return "executed!"
}
return "blargh, never got here!"
end
# Prints "executed!"
foo()Kotlin disallows break and continue inside blocks:
fun unless(condition: Boolean, block: () -> Unit) {
if (condition) {
block()
}
}
fun main(args: Array<String>) {
println("Hello, world!")
while (true) {
unless(true) {
println("hello")
// Error:(11, 12) 'break' or 'continue' jumps
// across a function or a class boundary
// break
// 'return' is not allowed here
// return
}
}
}fun case(condition: Boolean, block: () -> Unit) {
println("This gets called!")
block()
}
fun select(condition: Boolean, block: () -> Unit) {
block()
}
fun main(args: Array<String>) {
var expr: Boolean = true;
select (expr) {
case (true) {
println("Totally true")
}
case (true) {
println("Totally false")
}
}
}Interesting use case in constructors for Ruby:
https://mixandgo.com/blog/mastering-ruby-blocks-in-less-than-5-minutes
let car = new Car() {
::color = 1;
::size = "large";
}Could that lead to something like abstract classes?
sort([2, 3, 1, 4], new Comparator() {
::compare do (a, b) {
return a < b;
}
})
Principle of Abstraction
Tennents Correspondence Principle
These lambdas are particularly interesting. With arrow functions, we went as far as disabling
break,continueandyieldfrom top level statements, but we leftreturn.In this formulation, there are a few more cases that break the principle:
return.thisisn't lexical.var? should we disallow it and force the block-scopedlet?throwsshould probably be supported.TODO(goto): figure out how kotlin is planning to introduce
breakandcontinueinto the closures.http://yehudakatz.com/2012/01/10/javascript-needs-blocks/
https://web.archive.org/web/20161123223104/http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
returnreturns from the outer scope rather than the inner one.