F# strange printfn problem - f#

I was playing around with F# (Visual Studio 2010 beta 1), and I wrote a little console script that asked the user to input 2 numbers and an operator and then executed it.
It works fine, apart from a tiny, but annoying thing: sometimes my printfn instructions are ignored. I placed breakpoints in the code to see that's indeed the case.
The code snippet:
let convert (source : string) =
try System.Int32.Parse(source)
with :? System.FormatException ->
printfn "'%s' is not a number!" source;
waitForExitKey();
exit 1
let read =
printfn "Please enter a number.";
System.Console.ReadLine
let num1 : int = read() |> convert // the printfn in the read function is run...
let num2 : int = read() |> convert // ... but here is ignored
This is not the complete source of course, but I think that'll be enough. If you need the complete source just let me know.
So my question is pretty simple: what causes this issue with printfn? Am I doing something wrong?
Thanks in advance,
ShdNx

This page has a partial explanation of what's going on, but the short and sweet version is that F# will execute any value on declaration if it doesn't take parameters.
let read =
printfn "Please enter a number."
System.Console.ReadLine
Since read doesn't take any parameters, its executed immediately on declaration and binds the return value of the function to the identifier read.
Incidentally, your return value happens to be a function with the type (unit -> string). This results because F# automatically curries functions if they aren't passed all of their parameters. ReadLine expects one unit parameter, but since it isn't passed on, you actually bind read to the ReadLine function itself.
The solution is as follows:
let read() = // read takes one unit parameter
printfn "Please enter a number."
System.Console.ReadLine() // pass paramter to ReadLine method
Since read takes one parameter, its re-evaluated each time its called. Additionally, we're passing a parameter to ReadLine, otherwise we'll just return the ReadLine function as a value.

I understand that this can be confusing. In your example, printfn runs earlier than you think. It will actually execute even without the call to read(), i.e., comment out the last two lines and you will still see a message being printed.
I think your intention is something like this:
let read() =
printfn "Please enter a number.";
System.Console.ReadLine()
This will create a "reusable" function instead of binding a function to an identifier as in your original example.
As a sidenote, the use of semi-colons here is optional so you can just write:
let read() =
printfn "Please enter a number."
System.Console.ReadLine()

Related

How to access shadowed DXL variables/functions?

I encountered an error in a script I was debugging because somebody had created a variable with a name matching a built-in function, rendering the function inaccessible. I got strange errors when I tried to use the function, like:
incorrect arguments for (-)
incorrect arguments for (by)
incorrect arguments for ([)
incorrect arguments for (=)
Example code:
int length
// ...
// ...
string substr
string str = "big long string with lots of text"
substr = str[0:length(str)-2]
Is there a way to access the original length() function in this situation? I was actually just trying to add debug output to the existing script, not trying to modify the script, when I encountered this error.
For now I have just renamed the variable.
Well, in the case that you had no chance to modify the code, e.g. because it is encrypted you could do sth like
int length_original (string s) { return length s }
<<here is the code of your function>>
int length (string s) {return length_original s }

How should I terminate a func in F#

let Trans(DirPath:string) =
if ( numOfVmFiles(DirPath) > 1) then //if there are more than 1 vm files in the directory
Init("")
let VmFiles = ListOfVmFiles(DirPath)
for VmFile in VmFiles do // for each vm file
ReadFile(VmFile)
I got this error:
Error Block following this 'let' is unfinished. Expect an expression.
what should I write?thznk u
The existing answers give you good hints on how to write your code better. You said you are getting an error:
Block following this 'let' is unfinished. Expect an expression.
This typically indicates missing = or wrong indentation after the end of your function (or some easy to miss syntax error like that). In the snippet you posted, all syntax looks good to me, so I suspect there is something wrong elsewhere too. The following gives no errors:
let numOfVmFiles a = 0
let Init a = ()
let ListOfVmFiles a = []
let ReadFile a = ()
let Trans(DirPath:string) =
if ( numOfVmFiles(DirPath) > 1) then
Init("")
let VmFiles = ListOfVmFiles(DirPath)
for VmFile in VmFiles do // for each vm file
ReadFile(VmFile)
You get two warnings - because variable names should be camelCase rather than PascalCase, but no error. As others said, you should probably make Init and ReadFile return something and then you need to collect the results (to make your code more functional), but that's a separate problem.
The problem is that the function does not return a value. Functions must always return a value. If there is nothing to return, return unit. You can return unit as ().
I made some possibly incorrect assumptions here but I tried to make clear what they were. On the trans function I also show how you can specify the return type. It is usually best to let the compiler infer the type until it cannot. Hover over the functions and see what the compiler is telling you about the types. string -> int -> string list means a function takes a string and an int and returns a list of strings.
let init dirName = () //unit is returned... kind of like void but is actually a return value
let listOfVmFiles dirName = ["some";"files"] // list of string
let readFile path = "content of file" //string
let trans(dirPath:string) : string list = // takes a string and returns a list of string represented as string-> string list
let vmFiles = listOfVmFiles(dirPath) // get files from path
if(vmFiles.Length > 1) then init("") // init if more than 1 file
List.map readFile vmFiles // return a list of the content of the files
If a function is performing a side-effect and does not return something then it can be done like so:
let trans(dirPath:string) : unit =
let vmFiles = listOfVmFiles(dirPath)
if(vmFiles.Length > 1) then init("")
List.map readFile vmFiles |> ignore //ignore the result
()
This ignores the result of mapping the readFile function over the list and then returns unit using ().
I recommend fsharp for fun and profit for learning fsharp.
Hope this helps and good luck. Although the syntax seems weird initially stick with it. It's great!
In F# a function returns the value of the last expression it evaluated.
In your particular case it returns unit (), because a for loop returns unit unless its body yields values (in which case you would need to wrap it in a seq).
As mentioned in the comments, this code parses ok- your issue is with an unfinished expression elsewhere in your code.
The rewrite by Devon Buriss is a good example of best practices:
Explicitly declare your function return value in the signature.
ignore function return values if the function is only called for side effects (eg readFile and init("")).
Prefer functional behaviors such as map over imperatives such as for .. do.
As an aside, relying so heavily on side effects is likely to cause you difficulties elsewhere. A more common practice with data crunching is do have a function like readFile return file contents as a seq, and pipe the result to downstream processing:
List.map readFile vmFiles
|> seq.Concat // concatenate the file outputs
|> processContents
Whether or not this is the right thing for you depends on what exactly you intend to do with the contents of each file.

F# Function where given a string and an integer it returns a char at position(integer) from the string

I have a function that returns the char at a location:
let NthChar inpStr indxNum =
if inpStr.Length >=1 then printfn "Character %s" inpStr.[indxNum];
else printfn "Not enough arguments"
Error for
inpStr.Length
and
inpStr.[indxNum]
Error trace:
Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
You're trying to "dot into" something and access it's indexer. Except that when you do this the type of that something is still unknown (maybe it doesn't have an indexer)
So you got that rather explicit error message which also gives you the way to remove it, simply add a type annotation :
// code shortened
let nthChar (inpStr : string) indxNum = inpStr.[indxNum]
// alternative syntax
let nthChar inpStr indxNum = (inpStr : string).[indxNum]

Why is F# inferring my function implements IComparable?

I'm a hobbyist programmer (cook by trade) that's currently trying to teach myself F# and functional programming in general.
Anyway, I was fooling around with DeflateStream and wrote the following two functions:
let Compress compressMe =
let ds = new DeflateStream(File.Create("compressed.txt"), CompressionMode.Compress)
File.OpenRead(compressMe).CopyTo(ds)
ds.Close()
let Decompress =
let ds = new DeflateStream(File.OpenRead("compressed.txt"), CompressionMode.Decompress)
ds.CopyTo(File.Create("decompressed.txt"))
ds.Close()
In the body of the main function they are called one right after the other like this:
Compress args.[0]
Decompress
However, if compressed.txt doesn't exist when the program is run Decompress throws a FileNotFoundException which is surprising because the only thing that could throw this is the call to File.OpenRead("compress.txt"). After about an hour I figured out that Decompress was implementing IComparable and was being executed before the call to it in the main function. I found that by changing its definition to let Decompress () = [...] it no longer implemented IComparable and my code executed as it was intended to. Can anyone tell me why F# was infering IComparable and why such and inference would cause the function to execute before the main function marked with [<EntryPoint>]? Also, please forgive the imperitive style of my code, I'm incredibly new at this.
Thanks in adavance.
I'm not entirely sure about the IComparable bit, but the issue you have is that without the parentheses, the compiler is treating Decompress as a value not a function. This would be similar to if you had written.
let compressedName = "compressed.txt"
in that case, compressedName is now a value. Adding the parentheses tells the compiler that this is a function whose code must be called each time the function is rather than a value initialized once (before the entry point) by the code you wrote.
When you write something like
let value =
//some long calculation
[<Entrypoint>]
let main args = ...
The compiler executes the long calculation before main. This is because you probably use the value later in your code. To suppress this, as you found, you need to use let value() = ....
I am not sure where Icomparable is coming from, but this is the key to what is happening.
Note, if you write
let a = ...
let b = ...
The compiler will gurantee a is calculated before b executes.
As others have pointed out, the absence of parentheses in the declaration is significant.
let Decompres = ...
declares a variable of type unit. This type is used to represent "data" (even if this data doesn't encode much information), and it implements IComparable like any other data-oriented type.
let Decompress() = ...
declares a function, and functions in F# are not comparable, probably because there is no universally accepted notion of equality on functions.
I can't see that the "IComparable-ness" of Decompress had anything to do with the exception you got.

Visual studio - f# - Error FS0588 : Block following this 'let' is unfinished. Expect expression

i have got several times , trying to implement different functions, the message you see as title. I would like to know if anyone can tell me the general meaning (and reason) of this error message. As i mentioned before, i have got the problem several times and manage to fix it, but still didnt get the exact reason, so i will not post any specific code.
Thank you in advance
The most common case when you may get this error is when you write let binding that is not followed by an expression that calculates the result. In F#, everything is an expression that returns some result, so if you write let a = 10 it is generally not a valid expression. To make it valid, you need to return something:
let foo () =
let a = 10
() // return unit value (which doesn't represent any information)
The only exception where you can write just let a = 10 is a global scope of an F# source file - for example, inside a module declaration or in an F# script file. (This is why the declaration of foo above is valid).
It is difficult to give any advice without seeing your code, but you probably have a let declaration that is not followed by an F# expression.
Out of curiosity, the following example shows that let can really be used inside an expression (where it must return some meaningful result):
let a = 40 + (let a = 1
a + a)

Resources