In a Haskell tutorial I ran across the following code:
do [...]
let atom = [first] ++ rest
return $ case atom of
Note that the let expression does not have an in block. What is the scope of such a let expression? The next line?
Simply put, it's scoped "from where it's written until the end of the do".
Note that within a do statement, let is handled differently.
According to http://www.haskell.org/haskellwiki/Monads_as_computation#Do_notation , it is interpreted as follows:
do { let <decls> ; <stmts> }
= let <decls> in do { <stmts> }
The scope is the rest of the do block.
See §3.14 of the Haskell Report (specifically, the fourth case in the translation block). (Yes, this is the section about do blocks, because let without in is only valid inside a do block, as Porges points out.)
Related
The purpose of this code is to loop from 1 to 4 and stop.
My question is why if I add or leave the semicolon in the else statement (;) it doesn't matter... the code compile just fine. What's the best approach? Add the semicolon or leave it?
fn main() {
let mut var = 1;
loop{
println!("It is now {}", var);
if var >= 4 {
println!("Finished the loop");
break;
} else {
var = var + 1;
}
}
}
In general, the trailing semicolon determines the return value of the block. If you leave the semicolon out, the return value is the value of the last expression in the block. With the semicolon included, the return value is always Rust's unit value, the empty tuple () (except when the block contains an expression that does not return, in which case the return type is the "never" type !, which does not have any values).
In this particular case, there is no semantic difference. Assignment expressions return the unit value as well, i.e. (var = var + 1) == (). However, this is more or less a coincidence. You don't actually want to return any value in that statement, so including the semicolon makes your intention much clearer.
The generated binary will be exactly be the same here but don't just think about the compiler, think about the humans reading the code too.
The absence of a semicolon at the end of the last statement of a block has an implied meaning: the value is returned from the block, the value matters.
If the value isn't used, don't return it. Put that semicolon to help humans read the code.
If you don't put that semicolon and configure Clippy to be very annoying, you'll get a warning too due to the semicolon_if_nothing_returned rule.
I really appreciate the Raku's &?BLOCK variable – it lets you recurse within an unnamed block, which can be extremely powerful. For example, here's a simple, inline, and anonymous factorial function:
{ when $_ ≤ 1 { 1 };
$_ × &?BLOCK($_ - 1) }(5) # OUTPUT: «120»
However, I have some questions about it when used in more complex situations. Consider this code:
{ say "Part 1:";
my $a = 1;
print ' var one: '; dd $a;
print ' block one: '; dd &?BLOCK ;
{
my $a = 2;
print ' var two: '; dd $a;
print ' outer var: '; dd $OUTER::a;
print ' block two: '; dd &?BLOCK;
print "outer block: "; dd &?OUTER::BLOCK
}
say "\nPart 2:";
print ' block one: '; dd &?BLOCK;
print 'postfix for: '; dd &?BLOCK for (1);
print ' prefix for: '; for (1) { dd &?BLOCK }
};
which yields this output (I've shortened the block IDs):
Part 1:
var one: Int $a = 1
block one: -> ;; $_? is raw = OUTER::<$_> { #`(Block|…6696) ... }
var two: Int $a = 2
outer var: Int $a = 1
block two: -> ;; $_? is raw = OUTER::<$_> { #`(Block|…8496) ... }
outer block: -> ;; $_? is raw = OUTER::<$_> { #`(Block|…8496) ... }
Part 2:
block one: -> ;; $_? is raw = OUTER::<$_> { #`(Block|…6696) ... }
postfix for: -> ;; $_ is raw { #`(Block|…9000) ... }
prefix for: -> ;; $_ is raw { #`(Block|…9360) ... }
Here's what I don't understand about that: why does the &?OUTER::BLOCK refer (based on its ID) to block two rather than block one? Using OUTER with $a correctly causes it to refer to the outer scope, but the same thing doesn't work with &?BLOCK. Is it just not possible to use OUTER with &?BLOCK? If not, is there a way to access the outer block from the inner block? (I know that I can assign &?BLOCK to a named variable in the outer block and then access that variable in the inner block. I view that as a workaround but not a full solution because it sacrifices the ability to refer to unnamed blocks, which is where much of &?BLOCK's power comes from.)
Second, I am very confused by Part 2. I understand why the &?BLOCK that follows the prefix for refers to an inner block. But why does the &?BLOCK that precedes the postfix for also refer to its own block? Is a block implicitly created around the body of the for statement? My understanding is that the postfix forms were useful in large part because they do not require blocks. Is that incorrect?
Finally, why do some of the blocks have OUTER::<$_> in the but others do not? I'm especially confused by Block 2, which is not the outermost block.
Thanks in advance for any help you can offer! (And if any of the code behavior shown above indicates a Rakudo bug, I am happy to write it up as an issue.)
That's some pretty confusing stuff you've encountered. That said, it does all make some kind of sense...
Why does the &?OUTER::BLOCK refer (based on its ID) to block two rather than block one?
Per the doc, &?BLOCK is a "special compile variable", as is the case for all variables that have a ? as their twigil.
As such it's not a symbol that can be looked up at run-time, which is what syntax like $FOO::bar is supposed to be about afaik.
So I think the compiler ought by rights reject use of a "compile variable" with the package lookup syntax. (Though I'm not sure. Does it make sense to do "run-time" lookups in the COMPILING package?)
There may already be a bug filed (in either of the GH repos rakudo/rakudo/issues or raku/old-issues-tracker/issues) about it being erroneous to try to do a run-time lookup of a special compile variable (the ones with a ? twigil). If not, it makes sense to me to file one.
Using OUTER with $a correctly causes it to refer to the outer scope
The symbol associated with the $a variable in the outer block is stored in the stash associated with the outer block. This is what's referenced by OUTER.
Is it just not possible to use OUTER with &?BLOCK?
I reckon not for the reasons given above. Let's see if anyone corrects me.
If not, is there a way to access the outer block from the inner block?
You could pass it as an argument. In other words, close the inner block with }(&?BLOCK); instead of just }. Then you'd have it available as $_ in the inner block.
Why does the &?BLOCK that precedes the postfix for also refer to its own block?
It is surprising until you know why, but...
Is a block implicitly created around the body of the for statement?
Seems so, so the body can take an argument passed by each iteration of the for.
My understanding is that the postfix forms were useful in large part because they do not require blocks.
I've always thought of their benefit as being that they A) avoid a separate lexical scope and B) avoid having to type in the braces.
Is that incorrect?
It seems so. for has to be able to supply a distinct $_ to its statement(s) (you can put a series of statements in parens), so if you don't explicitly write braces, it still has to create a distinct lexical frame, and presumably it was considered better that the &?BLOCK variable tracked that distinct frame with its own $_, and "pretended" that was a "block", and displayed its gist with a {...}, despite there being no explicit {...}.
Why do some of the blocks have OUTER::<$_> in them but others do not?
While for (and given etc) always passes an "it" aka $_ argument to its blocks/statements, other blocks do not have an argument automatically passed to them, but they will accept one if it's manually passed by the writer of code manually passing one.
To support this wonderful idiom in which one can either pass or not pass an argument, blocks other than ones that are automatically fed an $_ are given this default of binding $_ to the outer block's $_.
I'm especially confused by Block 2, which is not the outermost block.
I'm confused by you being especially confused by that. :) If the foregoing hasn't sufficiently cleared this last aspect up for you, please comment on what it is about this last bit that's especially confusing.
During compilation the compiler has to keep track of various things. One of which is the current block that it is compiling.
The block object gets stored in the compiled code wherever it sees the special variable $?BLOCK.
Basically the compile-time variables aren't really variables, but more of a macro.
So whenever it sees $?BLOCK the compiler replaces it with whatever the current block the compiler is currently compiling.
It just happens that $?OUTER::BLOCK is somehow close enough to $?BLOCK that it replaces that too.
I can show you that there really isn't a variable by that name by trying to look it up by name.
{ say ::('&?BLOCK') } # ERROR: No such symbol '&?BLOCK'
Also every pair of {} (that isn't a hash ref or hash index) denotes a new block.
So each of these lines will say something different:
{
say $?BLOCK.WHICH;
say "{ $?BLOCK.WHICH }";
if True { say $?BLOCK.WHICH }
}
That means if you declare a variable inside one of those constructs it is contained to that construct.
"{ my $a = "abc"; say $a }"; # abc
say $a; # COMPILE ERROR: Variable '$a' is not declared
if True { my $b = "def"; say $b } # def
say $b; # COMPILE ERROR: Variable '$b' is not declared
In the case of postfix for, the left side needs to be a lambda/closure so that for can set $_ to the current value.
It was probably just easier to fake it up to be a Block than to create a new Code type just for that use.
Especially since an entire Raku source file is also considered a Block.
A bare Block can have an optional argument.
my &foo;
given 5 {
&foo = { say $_ }
}
foo( ); # 5
foo(42); # 42
If you give it an argument it sets $_ to that value.
If you don't, $_ will point to whatever $_ was outside of that declaration. (Closure)
For many of the uses of that construct, doing that can be very handy.
sub call-it-a (&c){
c()
}
sub call-it-b (&c, $arg){
c( $arg * 10 )
}
for ^5 {
call-it-a( { say $_ } ); # 0 1 2 3 4
call-it-b( { say $_ }, $_ ); # 010203040
}
For call-it-a we needed it to be a closure over $_ to work.
For call-it-b we needed it to be an argument instead.
By having :( ;; $_? is raw = OUTER::<$_> ) as the signature it caters to both use-cases.
This makes it easy to create simple lambdas that just do what you want them to do.
This question already has answers here:
How do I access a variable outside of an `if let` expression?
(3 answers)
Closed 3 years ago.
I am trying to avoid running a function if the user presents some information.
if let Some(app) = app.subcommand_matches("Download") {
if app.is_present("Server") {
let best = app.value_of("Server").unwrap();
} else {
let best = server::best_server("3").unwrap().to_owned();
let best = best.id.as_str();
};
let bytes = app.value_of("bytes").unwrap_or("100000024");
let dl = server::download(best, bytes).unwrap();
println!("Download Results {:#?} mbps", dl);
}
I would expect the code to only run best_server if app.is_present exists.
Why does assigning "best" inside the if statement end in the following error :
--> src/main.rs:80:35
|
80 | let dl = server::download(best.id.as_str(), bytes).unwrap();
| ^^^^ not found in this scope
When you use the let keyword you are declaring the given variable within the scope that the statement appears. The scope starts from the enclosing opening brace { and ends at the enclosing closing brace }.
So this:
if app.is_present("Server") {
let best = app.value_of("Server").unwrap();
}
declares a variable best, but that variable does not exist after the closing brace. That's why the error you are getting says the variable does not exist.
One way to fix this is to take advantage of the fact that an if statement has a value: the value of the last expression within the selected branch. So you could say:
let best = if app.is_present("Server") {
app.value_of("Server").unwrap()
} else {
server::best_server("3").unwrap().to_owned().id.as_str()
};
Note the lack of a ; on the end of the expressions within the if statement branches. If there was a semicolon there then the value of the expression would not be assigned.
I'm running GHC version 7.8.3 on Windows 7.
Ok, this is not about fancy code snippets. I'm just trying not be a noob here and actually compile something in a way that vaguely resembles the structure of side-effect languages.
I have the following code:
main =
do {
let x = [0..10];
print x
}
I've learned here, that the keyword do is a fancy syntactic sugar for fancy monadic expressions. When I try to compile it, I get the following error:
main.hs:4:1: parse error on input 'print'
And I've learned in this other question, that tabs in Haskell are evil, so I've tried to omit them:
main =
do {
let x = [0..10];
print x
}
And I've failed miserably, because the parse error persists.
I've also learned here, that print is a syntactic sugar for the fancy equivalent:
main =
do {
let x = [0..10];
putStrLn $ show x
}
But then I get this error instead:
main.hs:4:9: parse error on input 'putStrLn'
Trying to face my despair, I've tried to omit the let keyword, after reading this answer:
main =
do {
x = [0..10];
print x
}
And then I get:
main.hs:4:1: parse error on input '='
And in a final useless attempt, I've even tried to omit the ';' like this:
main =
do {
let x = [0..10]
print x
}
And got:
main.hs:4:1: parse error on input 'print'
So,
How to properly use monadic expressions in Haskell without getting parse errors? Is there any hope?
It took me a while to see what was actually going on here:
main =
do {
let x = [0..10];
print x
}
The above looks as if we have a do with two statements, which is perfectly fine. Sure, it is not common practice to use explicit braces-and-semicolons when indentation implicitly inserts them. But they shouldn't hurt... why then the above fails parsing?
The real issue is that let opens a new block! The let block has no braces, so the indentation rule applies. The block starts with the definition x = [0..10]. Then a semicolon is found, which promises that another definition is following e.g.
let x = [0..10] ; y = ...
or even
let x = [0..10] ;
y = ... -- must be indented as the x above, or more indented
However, after the semicolon we find print, which is even indented less than x. According to the indentation rule, this is equivalent to inserting braces like:
main =
do {
let { x = [0..10]; }
print x
}
but the above does not parse. The error message does not refer to the implicitly inserted braces (which would be very confusing!), but only to the next line (nearly as confusing in this case, unfortunately).
The code can be fixed by e.g. providing explicit braces for let:
main = do { let { x = [0..10] };
print x }
Above, indentation is completely irrelevant: you can add line breaks and/or spaces without affecting the parsing (e.g. as in Java, C, etc.). Alternatively, we can move the semicolon below:
main = do { let x = [0..10]
; print x }
The above semicolon is on the next line and is less indented than x, implicitly inserting a } which closes the let block. Here indentation matters, since let uses the indentation rule. If we indent the semicolon more, we can cause the same parse error we found earlier.
Of course, the most idiomatic choice is using the indentation rule for the whole code:
main = do let x = [0..10]
print x
I was about to say, with no useful information, that
main = do
let x = [0..10]
print x
Was working for me, but I'm now off to read about the in within the braces.
As a slight aside, I found http://echo.rsmw.net/n00bfaq.html quite handy for reading about identation/formatting.
main = do let x = [0..10]
print x
works for me
and so does
main = do { let x = [0..10]
in print x }
I think you're trying to mix some different syntax options up.
In the example below why must I place (let one) next to .Success for the code to work? Why is it not (let = one) or just (one)? I am trying to understand the syntax.
enum Status {
case Success(String)
case Failure(String)
}
let downloadStatus = Status.Failure("Network connection unavailable")
switch downloadStatus {
case .Success(let one):
println(one)
case .Failure(let two):
println(two)
}
This is known as value binding. The value contained in the enum value is temporarily bound to the variable in the case. If you use let the value will be bound to a constant and if you use var it will be bound to a variable.
let downloadStatus = Status.Failure("Network connection unavailable")
switch downloadStatus {
case .Success(let one):
println(one)
case .Failure(var two):
two += "!!!" // two is a var, so I can modify it
println(two)
}
I am trying to understand the syntax
There is nothing to understand. That simply is the syntax for extracting the associated value from an enum case. It's like the masks when you play Calvinball - no one is allowed to question them.
Now, you might object that this syntax is kind of clumsy and annoying. You have to go through the rigmarole of a switch statement just to pull out the associated value?!? That is a perfectly valid complaint, people have been making it since Swift was unveiled in June 2014, and the folks at Apple know that it's kind of weird and may change it in future. But for now, that's the syntax, and that's all there is to know about it.