Is there a reason why the exception is displayed before the function has even started in the following?
let listCharacters (text:string) =
let stripv3 = text.Split([|' '|], System.StringSplitOptions.RemoveEmptyEntries) |> System.String.Concat
for i in 0..2..stripv3.Length do
let char = stripv3.Chars(i)
if char <> ' ' then
printfn "%c" char
listCharacters "honey badger is a badass"
Produces the following output:-
System.IndexOutOfRangeException: Index was outside the bounds of the array.
h
n
y
a
g
r
s
b
d
s
Interestingly if I add a try..with any operations within the with occurs in order, any ideas why this is?
To summarise the comments above the problem is specific to the IDE and not to the language.
In this instance the issue was only seen in the Visual Studio F# Interactive view.
When executed fully or run via Visual Studio FSI.exe the exception was the last item to be output.
Related
F# noob here. This might be something stupid, please forgive my lack of understanding.
I am using FSharp.Data to access SQL on the back end.
Have the following MSTest code:
[<TestMethod>]
member this.EmailReads_Test_InsertOneGetAll () =
let initialCount = TblEmailReadHelper.all() |> Seq.length
TestTool.tblEmailReadDbCreate |> ignore // creates new record
let lastCount = TblEmailReadHelper.all() |> Seq.length
Assert.IsTrue( lastCount > initialCount, lastCount.ToString() + " <= " + initialCount.ToString())
If I run the Test Explorer and try to debug this, code works fine.
If I run the Test Explorer in run with other tests I get the following output
Assert.IsTrue failed. 9 <= 9
Which leads me to believe the line
let initialCount = TblEmailReadHelper.all() |> Seq.length
Is getting executed when I am doing the Assert
Assert.IsTrue( lastCount > initialCount, la...
Instead of the beginning of the test.
I know for a fact the add works and I am not running tests in parallel, so other tests are not messing with my count, but I know also when the first count happens it is getting the record that was added.
What simple concept I am not aware of?
Playing with F# and it seems that i cannot find out what is wrong.
FS0003 This value is not a function and cannot be applied. Did you
forget to terminate a declaration?
evaporator 25.0 10.0 10.0
let evaporator (volumeMl:double) (evapPerDaydouble:double) (threshold:double):int =
let mutable counter = 0
let mutable currentVolume = volumeMl
while (currentVolume > (volumeMl * (threshold / 100.))) do
currentVolume <- currentVolume - ((currentVolume * threshold / 100.))
counter <- (counter + 1)
counter
let result = evaporator 25.0 10.0 10.0
printfn "%f" result
Update
modified code with ;;
let result = evaporator 25.0 10.0 10.0;;
And it is working like expected. Strange.
Update 2
The only problem with your initial code is that you've used printfn "%f" instead of printfn "%i".
If your issue is fixed by adding ;; it makes me think you are running this in FSI and seeing the compiler error in FSI. This is fine but perhaps you are typing or pasting code directly in to the FSI prompt?
My advice to everyone starting with F#, and even experienced F# users, is to never type or paste code into FSI. Write code in your editor, select it, and send it to FSI. This way you don't need to worry about remembering semi-colons, and you get compiler errors and suggestions as you type. I have worked in F# day-to-day for years and never had the need to type directly into FSI.
Also, don't forget to re-run all your function and type definitions in FSI if you change them. It's best to reset FSI and start with clean state if you're seeing confusing errors.
This can happen if there is leading whitespace. The following example produces this error.
let x = 1
let y = 2
Removing the leading whitespace resolves the issue.
let x = 1
let y = 2
Setting your editor to show whitespace will help detect this.
The reason adding ;; also helps is because it terminates the blocked code created by the leading whitespace.
UPDATE:
I now realize that the question was stupid, I should have just filed the issue. In hindsight, I don't see why I even asked this question.
The issue is here: https://github.com/fsharp/FSharp.Compiler.Service/issues/544
Original question:
I'm using FSharp Compiler Services for parsing some F# code.
The particular piece of code that I'm facing right now is this:
let f x y = x+y
let g = f 1
let h = (g 2) + 3
This program yields a TAST without the (+) call on the last line. That is, the compiler service returns TAST as if the last line was just let h = g 2.
The question is: is this is a legitimate bug that I ought to report or am I missing something?
Some notes
Here is a repo containing minimal repro (I didn't want to include it in this question, because Compiler Services require quite a bit of dancing around).
Adding more statements after the let h line does not change the outcome.
When compiled to IL (as opposed to parsed with Compiler Services), it seems to work as expected (e.g. see fiddle)
If I make g a value, the program parses correctly.
If I make g a normal function (rather than partially applied one), the program parses correctly.
I have no priori experience with FSharp.Compiler.Services but nevertheless I did a small investigation using Visual Studio's debugger. I analyzed abstract syntax tree of following string:
"""
module X
let f x y = x+y
let g = f 1
let h = (g 2) + 3
"""
I've found out that there's following object inside it:
App (Val (op_Addition,NormalValUse,D:\file.fs (6,32--6,33) IsSynthetic=false),TType_forall ([T1; T2; T3],TType_fun (TType_var T1,TType_fun (...,...))),...,...,...)
As you can see, there's an addition in 6th line between characters 32 and 33.
The most likely explanation why F# Interactive doesn't display it properly is a bug in a library (maybe AST is in an inconsistent state or pretty-printing is broken). I think that you should file a bug in project's issue tracker.
UPDATE:
Aforementioned object can be obtained in a debbuger in a following way:
error.[0]
(option of Microsoft.FSharp.Compiler.SourceCodeServices.FSharpImplementationFileDeclaration.Entity)
.Item2
.[2]
(option of Microsoft.FSharp.Compiler.SourceCodeServices.FSharpImplementationFileDeclaration.MemberOrFunctionOrValue)
.Item3
.f (private member)
.Value
(option of Microsoft.FSharp.Compiler.SourceCodeServices.FSharpExprConvert.ConvExprOnDemand#903)
.expr
In reading John Palmer's answer to What is the difference between mutable values and immutable value redefinition?, John notes that
This sort of redefinition will only work in fsi.
In working with F# Interactive (fsi) I guess I subconsciously knew it, but never paid attention to it.
Now that it is apparent, why the difference?
More specifically please explain how the internals are different between fsi and the compiler such that this occurs by design or result of differences?
If the answer can elaborate on the internal structures that hold the bindings that would be appreciated.
The semantics are consistent with the way FSI compiles interactive submissions to an FSI session: each interactive submission is compiled as module which is open to subsequent interactive submissions.
The following is close to what FSI actual does and illustrates how let binding shadowing works across interactive submissions:
FSI Submission #1: let x = 1;;
module FSI_1 =
let x = 1
open FSI_1 //FSI_1.x is now bound to 1 and available at the top level
FSI Submission #2: let x = 2;;
module FSI_2 =
let x = 2
open FSI_2 //FSI_1.x is now shadowed by FSI_2.x which is bound to 2 and available at the top level
You can see the actual details how how the dynamic FSI assembly is compiled by using reflection on the FSI_ASSEMBLY assembly within the FSI app domain. Each interactive submission is literally emitted as a module (.NET class) with the naming pattern FSI_####. FsEye uses these facts to discover the state of FSI top-level bindings: https://code.google.com/p/fseye/source/browse/tags/2.0.1/FsEye/Fsi/SessionQueries.fs#24
The key takeaway in regard to #JohnPalmer's answer is that top-level FSI definitions cannot be mutated, when they are "redefined" they are merely being shadowed. We can show this as follows:
> let x = 1;; //our original definition of x
val x : int = 1
> let f () = x;; //capture x
val f : unit -> int
> let x = 2;; //shadow our original definition of x
val x : int = 2
> f();; //returns the original x value, which is still 1 rather than 2
val it : int = 1
I just started learning F#, and tried a code from the wiki:
I prefer tabs to spaces, so I change the code a bit into this:
#indent "off"
open System
open System.Windows.Forms
let form = new Form(Visible=true, TopMost=true, Text="Welcome to F#")
let label =
let temp = new Label()
let x = 3 + (4 * 5)
temp.Text <- sprintf "x = %d" x
temp
form.Controls.Add(label)
[<STAThread>]
Application.Run(form)
The output is:
Microsoft (R) F# 2.0 Compiler build
4.0.30319.1 Copyright (c) Microsoft Corporation. All Rights Reserved.
fstest2.fs(1,1): warning FS0062: This
construct is for ML compatibility.
Conside r using a file with extension
'.ml' or '.mli' instead. You can
disable this warn ing by using
'--mlcompatibility' or '--nowarn:62'.
fstest2.fs(9,2): error FS0010:
Unexpected keyword 'let' or 'use' in
expression. Expected 'in' or other
token.
fstest2.fs(13,1): error FS0597:
Successive arguments should be
separated by spac es or tupled, and
arguments involving function or method
applications should be parenthesized
fstest2.fs(9,14): error FS0374:
Invalid expression on left of
assignment
fstest2.fs(16,1): error FS0010:
Unexpected identifier in definition
Guess the error is somewhere in the let label block, but couldn't figure it out.
If you use "#indent off", then you lose all the simpler whitespace-aware syntax, and have to go back to using e.g.
#indent "off"
open System
open System.Windows.Forms
let label =
let temp = new Label() in
let x = 3 + (4 * 5) in
temp.Text <- sprintf "x = %d" x;
temp;;
let form =
let f = new Form() in
f.Controls.Add(label);
f;;
[<STAThread>]
do Application.Run(form)
with semicolons and ins and all other kinds of syntactic noise everywhere. You'll probably be happier just having your editor convert tabs to spaces (and having a smart editor that can treat spaces as though they are tabs, e.g. so that backspace can back up one tab-stop).
This topic was already discussed in this StackOverflow question. As Brian explains, turning off the "lightweight" syntax means that you'll have to write in the OCaml-compatible syntax.
I believe that in most of the cases, the syntax based on indentation is more readable (and so it is worth switching from tabs to spaces). However, the syntax with additional noise (such as in and ;;) reveals more about the structure of the language, so it may be useful to play with it briefly while learning F#.
The following example shows all the additional things that you need to write:
let add a b c =
let ab = a + b in // 'in' keyword specifies where binding (value 'ab') is valid
printfn "%d" ab; // ';' is operator for sequencing expressions
c - ab;; // ';;' is end of a function declaration
For more discussions, see also this post.