This needs some unpacking.
First, your core mistake: System.Char.ToUpper
is a function. It takes a char and returns another char. It doesn't somehow "update" its argument to a new value.
let x = 'a'
let y = System.Char.ToUpper x // y = 'A', x = 'a'.
In the above code, I give name y
to the result of the function. The value of y
is 'A'
, but the value of x
is still 'a'
. After calling the function, x
hasn't changed.
From this mistake, all the rest follows.
Second, List.iter
is a function that, for every element of a list, makes something "happen". It doesn't replace each element of a list with something new, nor does it create a new list. It just makes something happen for every element. The simplest example of such "something" is printing out to console:
List.iter (fun x -> printfn "%i" x) [1; 2; 3] // Prints "1", then "2", then "3"
Notice that this function takes two arguments: the function that represents the something that need to happen, and the list from which to take the elements. In your question, you seem to be missing the second argument. How would List.iter
know which list to use?
The first argument of List.iter
needs to be a function that returns unit
. This is a special type in F# that basically means "no value". When a function returns no value, it means that the only reason for calling it was to make something external happen (known in functional programming as "side-effect"). This is why List.iter
requires the function to return unit
- it's extra protection from accidentally supplying wrong function, just as you did, actually: the function you provided returns char
. This is why you receive the error that you receive.
Third, just like with ToUpper
, calling List.ofArray
doesn't somehow "update" x
to be a list. Instead, it returns a list. If you don't give that returned list a name, it will just be lost. Which means that the way you're calling List.ofArray
is futile.
What you actually need is to (1) take the sequence of characters in your string, then (2) convert it to a new sequence where each character is upper case, then (3) glue those characters back together to get a new string.
Step (1) is a no-op, because .NET strings are already sequences of chars (i.e. they implement IEnumerable<char>
). Step (2) is accomplished via a common operation called Seq.map
. It's an operation that converts a sequence to a new sequence by applying given function to every element. The "given function" in this case will be System.Char.ToUpper
. Step (3) can be accomplished via String.concat
, but you'd need to convert each char to a string first, because String.concat
takes a sequence of strings, not chars.
let chars = s
let upperChars = Seq.map System.Char.ToUpper chars
let strChars = Seq.map string upperChars
let result = String.concat "" strChars
Or this can be done in a shorter way, without giving each step's result a separate name, but by piping each result straight into the next operation:
let result =
s
|> Seq.map System.Char.ToUpper
|> Seq.map string
|> String.concat ""
And finally, there is actually a much shorter way to do it, but it's so ridiculously obvious, it feels like cheating.
The thing is, because strings are sequences, it kinda makes sense for them to have all the sequence operations. And guess what? They do! Specifically, there is a function String.map
, which does the same thing as Seq.map
, but for strings:
let result = String.map System.Char.ToUpper s