The JS-compiled F# compiler in the Fable REPL still has some issues. Please add here the snippets that are not compiling to be sure they get fixed.
Please check first if the code is compiling with fable-compiler. If it's also a bug there, report it with its own issue.
type ClassA (?x) =
let mutable x = defaultArg x 0.
member self.X
with get () = x
and set (value) =
x <- value
type Maybe<'a> =
| Just of 'a
| Nothing
let parseInt (x: string) =
try
let result = int x
Just result
with
| _ -> Nothing
match parseInt "20" with
| Just value -> printfn "%d" value
| Nothing -> printfn "Nothing Found"
@MangelMaxime @Zaid-Ajaj The above samples compile now in the REPL :) Please comment again in this issue if you find any other problem.
@alfonsogarciacaro got another one :)
```f#
open System.Collections.Generic
let fastFib (n: int) : int =
let a = (1.0 + sqrt 5.0) / 2.0
let b = (1.0 - sqrt 5.0) / 2.0
let power = float n
let result = ((a * power) - (b * power)) / sqrt 5.0
int result
let rec slowFib n =
match n with
| 0 -> 1
| 1 -> 1
| _ -> slowFib (n - 1) + slowFib (n - 2)
let memoizedFibonnaci =
let dict = new Dictionary
fun n ->
if dict.ContainsKey n then dict.[n]
else
let result = slowFib n
dict.Add(n, result)
result
[1..30]
|> List.map memoizedFibonnaci
|> List.iter (printfn "%d")
```
This runs correctly both on .NET and with fable locally (v0.7.42) but not in the repl
@Zaid-Ajaj @alfonsogarciacaro Here is a smaller repro:
let dict = new System.Collections.Generic.Dictionary<int, int>()
dict.[5]
@Zaid-Ajaj @ncave Thanks for the reports, keep 'em coming! This has been fixed (still the issue with dictionaries with custom equality), can you please check? I added Zaid-Ajaj's code to the samples :)
Please remember you must empty the browser cache to get the latest changes if compilation is still failing for you.
This is weird, this works
open System.Text.RegularExpressions
let parseFloat (input: string) : float option =
let regex = new Regex("[0-9]+[.[0-9]+]?")
if regex.IsMatch input
then System.Double.Parse(input) |> Some
else None
match parseFloat "3.35" with
| Some value -> printfn "Parsed %A" value
| None -> printfn "Nothing found"
And this works
open System.Text.RegularExpressions
let parseInt (input: string) : int option =
let regex = new Regex("[0-9]+")
if regex.IsMatch input
then System.Int32.Parse(input) |> Some
else None
match parseInt "35" with
| Some value -> printfn "Parsed %d" value
| None -> printfn "Nothing found"
BUT putting them together DOESN'T work
open System.Text.RegularExpressions
let parseInt (input: string) : int option =
let regex = new Regex("[0-9]+")
if regex.IsMatch input
then System.Int32.Parse(input) |> Some
else None
let parseFloat (input: string) : float option =
let regex = new Regex("[0-9]+[.[0-9]+]?")
if regex.IsMatch input
then System.Double.Parse(input) |> Some
else None
match parseFloat "3.35" with
| Some value -> printfn "Parsed %A" value
| None -> printfn "Nothing found"
match parseInt "35" with
| Some value -> printfn "Parsed %d" value
| None -> printfn "Nothing found"
The repl isn't happy either with [<Emit>]
open Fable.Core.JsInterop
[<Emit("isNaN(parseFloat($0)) ? null : parseFloat($0)")>]
let parseFloat (input: string) : float option = jsNative
match parseFloat "3.35" with
| Some value -> printfn "%A" value
| None -> printfn "Nothing found"
Hi @Zaid-Ajaj! Again, thanks a lot for catching these issues, you're helping to improve Fable's JS code generation!
The parsing code should work now (please remember to empty your browser cache). About the Emit attribute, it actually works. The culprit in that code is jsNative as we have a problem right now we're trying to solve to load the modules in Fable.Core. If you change the code to avoid jsNative it works:
open Fable.Core
[<Emit("isNaN(parseFloat($0)) ? null : parseFloat($0)")>]
let parseFloat (input: string) : float option = failwith "JS"
match parseFloat "3.35" with
| Some value -> printfn "%A" value
| None -> printfn "Nothing found"
@alfonsogarciacaro I am glad I can help :smile:
How about adding this sample named "Safe Wrappers" (wrapping js functions to return option type)?
open Fable.Core
[<Emit("isNaN(parseFloat($0)) ? null : parseFloat($0)")>]
let parseFloat (input: string) : float option = failwith "JS"
[<Emit("isNaN(parseInt($0)) ? null : parseInt($0)")>]
let parseInt (input: string) : int option = failwith "JS"
[<Emit("(function() { var prop = $1[$0]; return prop === undefined ? null : prop; })()")>]
let getProp<'t> (propName: string) (any: obj) : 't option = failwith "JS"
[<Emit("$2[$0] = $1")>]
let setProp<'t> (propName: string) (propValue: 't) (any: obj) : unit = failwith "JS"
match parseFloat "3.35" with
| Some value -> printfn "%A" value
| None -> printfn "Nothing found"
match parseInt "20" with
| Some value -> printfn "%A" value
| None -> printfn "Nothing found"
let x = new obj()
x |> setProp "message" "Hello there"
let propValue = getProp<string> "message" x
match propValue with
| Some value -> printfn "%s" value
| None -> printfn "Nothing found"
let unknownProp = getProp<string> "unknown" x
match unknownProp with
| Some value -> printfn "%s" value
| None -> printfn "Nothing found"
Absolutely! Can you PR to https://github.com/fable-compiler/fable-compiler.github.io? Sample file goes in repl_samples folder and then you add a reference here (the key must correspond to the sample file name).
One note about using Emit with duplicated arguments, though. There's a risk that you evaluate some expressions twice. Example:
open Fable.Core
[<Emit("isNaN(parseFloat($0)) ? null : parseFloat($0)")>]
let parseFloat (input: string) : float option = failwith "JS"
let generateStringFloat() =
printfn "Expensive operation"
"3.35"
match generateStringFloat() |> parseFloat with
| Some value -> printfn "%A" value
| None -> printfn "Nothing found"
Prints:
Expensive operation
Expensive operation
3.35
@alfonsogarciacaro Yeah I could use something like this,
[<Emit("(function() { var prop = $1[$0]; return prop === undefined ? null : prop; })()")>]
let getProp<'t> (propName: string) (any: obj) : 't option = failwith "JS"
I am defining a variable inside an IIFE but I was not sure if the scoping would behave the same way as I expect (not to collide with global vars)
@Zaid-Ajaj I would suggest something like this:
[<Emit("(x => isNaN(parseFloat(x)) ? null : parseFloat(x))($0)")>]
let parseFloat (input: string) : float option = failwith "JS"
[<Emit("((o, p) => p in o ? o[p] : null)($1,$0)")>]
let getProp<'t> (propName: string) (any: obj) : 't option = failwith "JS"
Actually this kind of functions should go in a separate JS file, but you cannot have separate JS files in the REPL right now. Or maybe I should do a double eval check for Emit expressions, Fable already does it for inline expressions...
This code hard-crashes the REPL:
module Json
open Fable.Core
type Property<'A, 'B> = {
Key: 'A
Value: 'B
}
type Json =
| String of string
| Number of float
| Object of array<Property<string, Json>>
| Array of array<Json>
| Boolean of bool
| Null
[<Import("ofString", "./json.js")>]
let _ofString:
(string -> Json) ->
(float -> Json) ->
(array<Property<string, Json>> -> Json) ->
(string -> Json -> Property<string, Json>) ->
(array<Json> -> Json) ->
(bool -> Json) ->
Json ->
(Json -> option<Json>) ->
option<Json> ->
(string -> option<Json>) = jsNative
let ofString: string -> option<Json> = _ofString String Number Object (fun key value -> { Key = key; Value = value }) Array Boolean Null Some None
Sorry it isn't a smaller reproduced test case. If you remove the last let ofString ... line then it works fine.
Maybe it's related to issue #787? Or it might be related to the [<Import("ofString", "./json.js")>]
@mizzle-mo says that it compiles fine with Fable 1.0, but it crashes Ionide in VSCode.
@forki says that it compiles fine with Fable 0.7.42
Just for fun, I tried compiling the above snippet in Edge:

(it aborted a few seconds later)
so it's a nice browser benchmark. let's stay positive here
Got some more code that doesn't work in the REPL, but it does work with Fable 1.0:
open Microsoft.FSharp.Reflection
let inline tagName (value: 'A): string =
let (case, _) = FSharpValue.GetUnionFields(value, typeof<'A>)
case.Name
type DU =
| Foo of int
| Bar of int
tagName (Foo 1)
In the REPL I get this:
F# project contains errors:
stdin.fsx(L1,0) : error FSHARP: internal error: Converting circular structure to JSON
stdin.fsx(L5,4) : error FSHARP: 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.
The REPL is some versions behind (I haven't touched it since before the Webpack integration), I need to update it, sorry!
The Memoized Fib fails in the latest REPL (master), here is a minimal repro:
let dict = new System.Collections.Generic.Dictionary<int, int>()
dict.Add(2, 3)
and the error:
F# project contains errors:
stdin.fsx(L1,0) : error FSHARP: internal error: Iterate2D
After a long digging exersize, seems to be related to #842
@alfonsogarciacaro All the examples above work properly now on the latest REPL in master, you can close this when you publish a newer REPL version.
amazing!
I do not know if this happens only in the REPL but when a module and a generic type have the same name the module is not created.
this runs:
type Val = | Constant of int
module Val =
let HelloVal() = "HelloVal"
printfn "Running = %A" <| Val.HelloVal()
this does not:
type Val<'a> = | Constant of int
module Val =
let HelloVal() = "HelloVal"
printfn "Running = %A" <| Val.HelloVal()
it crashes with this message
Uncaught TypeError: Val.HelloVal is not a function
at eval (eval at REPL.evaluate (repl.js:325), <anonymous>:49:49)
I'm guessing that because of the generic parameter it doesn't recognize the two names as the same.
@amieres Please see https://github.com/fable-compiler/Fable/issues/1231#issuecomment-344255640
Most helpful comment
@Zaid-Ajaj @alfonsogarciacaro Here is a smaller repro: