A pull request has been done with a new test for the Rust compiler. It verifies that a strange line can compile:
fn main() {
let val = !((|(..):(_,_),__#_|__)((&*"\\",'#')/**/,{})=={&[..=..][..];})//
;
assert!(!val);
}
What does this line do exactly?
Let's break it down! First, I reformatted the line to somewhat increase "readability".
let val = !(
(
|(..): (_, _), __#_| __
)(
(
&*"\\",
'#'
) /**/,
{}
)
==
{
&[..=..][..];
}
)//
;
It starts with let val = and ends with //<newline>;. So it's a simple let-binding of the form let val = ⟨v⟩;. Let's discuss what ⟨v⟩ does:
A negation via the not operator: !( ⟨_⟩ )
A comparison via ==: ⟨lhs⟩ == ⟨rhs⟩
⟨lhs⟩: a function call of a closure ( ⟨closure⟩ )( ⟨args⟩ )
⟨closure⟩: a closure definition |⟨first_param⟩, ⟨second_param⟩| ⟨body⟩
⟨first_param⟩: (..): (_, _). This parameter has a type annotation (_, _) meaning that it is a 2-tuple. The pattern (where normally, you would find a single name) is (..) which means: a tuple, but ignore all elements of it.
⟨second_param⟩: __#_. This is a pattern usually known from match bindings: name # pattern. So the actual pattern is _ (which doesn't bind anything) and the value is bound via # to the name __ (two underscores, which is a kind of valid identifier).
⟨body⟩: __. This is just the identifier which we bound the second parameter to. So the closure is basically equivalent to |_, x| x.
⟨args⟩: a list of two arguments with an inline comment /**/ in between: ⟨first_arg⟩/**/, ⟨second_arg⟩
⟨first_arg⟩: (&*"\\", '#'). This is simply a 2-tuple where the first element is a string containing a backslash and the second is the char '#'. The &* for the first element cancels out.
⟨second_arg⟩: {}. This is an empty block which has the type (). So as second argument, a unit is passed.
⟨rhs⟩: a braced block with one statement inside: { ⟨stmt⟩; }. Note that this is a statement, with a semicolon. This means the result is not returned from the block. Instead the block returns () just as the empty block {}.
⟨stmt⟩: an indexing expression { &⟨collection⟩[⟨index⟩] }.
⟨collection⟩: [..=..]. This is an array with one element. The element is ..= .. which is a RangeToInclusive where end of the range is the RangeFull written ...
⟨index⟩: ... This is just the RangeFull again.
So in summary: we compare the result of calling a closure to a braced block which evaluates to (). The closure is basically |_, x| x and the second argument we pass to it is {} (which evaluates to ()), so the whole closure calling expression evaluates to ().
This means the whole thing is equivalent to:
let val = !( () == () );, which is equivalent to:
let val = !( true );, which is equivalent to:
let val = false;
Related
I want to get a warning everywhere the equals operator '=' is used on struct-typed arguments. It seems that if I define this operator it does not overload, it just redefines '=' to only work for struct-typed arguments, which is not what I want.
[<Obsolete("Possible performance issue with '=' operator on structs. See https://github.com/dotnet/fsharp/issues/526.")>]
let inline (=) (x: ^T) (y: ^T) : bool when ^T: struct and ^T: (static member (=) : ^T * ^T -> bool) =
x = y
let a = obj()
let x = Guid.NewGuid()
let y = Guid.NewGuid()
a = a |> ignore // oh no - "A generic construct requires that the type 'obj' is a CLI or F# struct type."
x = y |> ignore // ok - gets desired warning
Can this be done in F# as is?
Update: found a possible workaround: simply use the [<NoEquality>] attribute on the affected structs; it does mean that all structs need to be annotated but at least it does help.
No, you cannot redefine a globally-scoped (i.e. non-member) function only for some cases. Once a function is in scope, it will always be used, there is no fallback to the previously defined one.
I saw the following line in my code, and I am not sure what it does as I haven't encountered the # operator before.
if let e#Err(_) = changed {
...
}
Can this line be written without the # operator, what would that look like?
It's a way to bind the matched value of a pattern to a variable(using the syntax: variable # subpattern). For example,
let x = 2;
match x {
e # 1 ..= 5 => println!("got a range element {}", e),
_ => println!("anything"),
}
According to https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#-bindings and https://doc.rust-lang.org/reference/patterns.html#identifier-patterns, it is used for simultaneously matching to the pattern to the right of the # while also binding the value to the identifier to the left of the #.
To answer your second question, Yes, it would look like
if let Err(_) = &changed {
// continue to use `changed` like you would use `e`
}
Note that in order to continue to use changed inside the body, you need to match for the reference &changed. Otherwise it will be moved and discarded (unless it happens to be Copy).
In Swift 2.1 the snippet below generates an error.
var str = "Hello, playground!"
// Success Case
if "!" == str.characters.last {
print("Tone it down please")
}
// Fail Case
let bang = "!"
if bang == str.characters.last { // this line won't compile
print("Tone it down please")
}
The compiler error says:
Binary operator '==' cannot be applied to operands of type 'String'
and '_Element?'
So what is the recommended way to use a constant as opposed to a literal in this situation? (I'm learning Swift, so please feel free to mention if there's a Swift-er way to handle this kind of comparison check.)
Thanks!
For your "Fail case", this is because str.characters.last is an Optional and is a Character, but bang is a String.
You can safely unwrap and compare with if let ... where, and use String() to change the Character to a String for the comparison:
if let last = str.characters.last where String(last) == bang {
print("Tone it down please")
}
As the error says, the first operator is a String, and the second one is an optional Character.
But you've already demonstrated that you know how to turn a string into an Character?, so lets use that:
if bang.characters.last == str.characters.last {
print("Tone it down please")
}
You know that bang.characters.last will just return "!", but now it will be of the same type as str.characters.last, so it will be trivial to compare them.
Thanks for the good discussion. Here's my own answer to improve the illustration by eliminating extraneous optionals, and to demonstrate both the good and the bad of type inference at play:
let a:String = "!" // type is String
let b:Character = "!" // type is Character
let c = "!".characters.last! // type is _Element
let bang = "!" // inferred type is String
if "!" == a { print("literal matches string") }
if "!" == b { print("literal matches Character") }
if "!" == c { print("literal matches _Element") }
if a == b { print("a matches b") } // Err: 'String' to 'Character'
if a == c { print("a matches c") } // Err: 'String' to '_Element'
if b == c { print("b matches c") } // OK: 'Character' to '_Element'
Conclusion: A literal consisting of a single quoted character can be recognized as a String or as a Character (or equivalently an _Element), if the context suggests it.
Importantly: The type of a constant is permanently established when it is declared. The type of a literal is inferred from its context, so the same literal may have different types in different contexts.
Flexible type inference afforded to a literal is not available with a constant.
Not sure if this is totally related, but I found this post as I had problems converting between characters.first, characters.last and Int.
In case this helps anyone:
let element = characters.first! // the ! is important
let myString = String(element)
let myInt = Int(myString) // may be nil if character is not an int
I'm trying to write my first Swift program, and I know this question has been asked before, but the answers using split aren't working for me. I'm using Xcode 6.4 and Swift 1.2.
I have a String named line.
If I write
let inputs = split(line) {$0 = " "}
as suggested at Swift: Split a String into an array, I get the error message "Cannot invoke 'split' with an argument list of type (String, ()->)"
If I write
let inputs = split(line, {find(" ",$0) != nil}, allowEmptySlices: false)
as suggested at split now complains about missing "isSeparator", I get the error message, "Missing argument for parameter 'isSeparator' in call."
If I jump to the definition of split, I find
func split<S : Sliceable, R : BooleanType>(elements: S, maxSplit: Int = default, allowEmptySlices: Bool = default, #isSeparator: #noescape (S.Generator.Element) -> R) -> [S.SubSlice]
I don't understand what the type of the last parameter is, which is perhaps the root of my problem. Can you tell me how I should call split, and even better can you explain what the parameter type is? Why isn't the type simply (S)->R? I am getting the line from a generator that reads a file line-by-line, if that makes any difference.
for line:String in reader! {
let inputs = split(line) {$0 = " "}
...
}
As said in the comments to the question, the correct way is to use the == operator instead of =.
The type (S.Generator.Element) -> R) must be interpreted in the light of the definition of split:
func split<S : Sliceable, R : BooleanType>
(elements: S,
maxSplit: Int = default,
allowEmptySlices: Bool = default,
#isSeparator: #noescape (S.Generator.Element) -> R)
-> [S.SubSlice]
The type of split is a generic one: in other words, it is a function that can take as first parameter any value that satisfy a generic type (or protocol) subtype of Sliceable, like String, and return a result which must be a subtype of BooleanType (for instance true or false, which are instances of Bool). So the last parameter is a function which gets as parameter a type which is Element of Generator of S (for instance Character) and returns a value of type R. And {$0 == " "} is exactly a predicate of this type, that has an (implicit) parameter ($0), and check if it is equal to the character " ".
I've got the following code which is supposed to count the numbers of times a character appears in a string.
def filter[T] (l: List[T], stays: T ⇒ Boolean): List[T] = {
if( l == Nil ) return Nil
if (stays(l.head) == true) l.head :: filter(l.tail, stays)
else filter(l.tail, stays)
}
def countChar(s: String): List[(Char, Int)] = {
if (s == "") Nil
else (s(0), s.count(_ == s(0))) :: countChar(filter(s.toList, _ == s(0)).mkString)
}
Now my problem is that in
filter(s.toList, _ == s(0))
I get the error of: missing parameter type. I understand that this comes from nesting the function?
How can I fix this to work? I know that String has some methods to do what I want but I'd like to use my own filter method.
That's a limitation of Scala compiler: it tries to figure out what type T in filter should be, using both arguments l and stays. But it fails because the type of stays argument is unspecified.
If you don't want to specify the type of stays argument every time (i.e., filter(s.toList, (_: Char) == s(0)), you can split filter's argument list into two:
def filter[T] (l: List[T])(stays: T ⇒ Boolean): List[T]
Then Scala will know that T is Char by the time it analyzes the type of stays. You can call this filter with filter(l.tail)(stays).