Heavily inspired by kotlin and groovy, what would the following syntax simplication enable?
a {}
// syntact sugar for
a(function() {});
?
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 break
break
can break out of any block. For example:
foo: { a = 1; break foo; a = 2 }
console.log(a) // 1
Apparently, 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;
}
})
Ruby's custom control structures
https://www.safaribooksonline.com/library/view/the-ruby-programming/9780596516178/ch08s08.html