Calling ES 3rd party script method from F# Fable - f#

I'm trying to get Fable to compile the following code correctly but am unable to do so:
module AppView
#r "../../../node_modules/fable-core/Fable.Core.dll"
open Fable.Core
open Fable.Import.Browser
open Fable.Core.JsInterop
[<Import("default", from="../../../js/3rd/riot.js")>]
module riot_js =
let mount:((string*obj)->array<obj>) = jsNative
type App
(
tagName:string
,state
,store
) =
member public x.AppTag =
(riot_js?mount ("app", state))
// does not compile: The value or constructor 'riot_js' is not defined.
// (riot_js.mount ("app", state))
// compiles wrongly to: riot_js.mount(["app", this.state]);
Trying riot_js?mount would magically cause riot_js to not exist any more and trying riot_js.mount compiles into riot_js.mount(["app", this.state]);.
Mount does not take one argument but 2 but it either won't transpile or transpile wrong.
For now I have one of the strangest looking solutions:
[<Emit("riot_js")>]
let riot_js (x: int): obj = jsNative
...
((riot_js 1)?mount ("app", state))
This returns an array but again Fable does not let me take the first element in a "normal" way:
((riot_js 1)?mount ("app", state))?[0]
Gives me red on [ with error Unexpected symbol '[' in expression. Expected identifier, '(' or other token.
And
((riot_js 1)?mount ("app", state)).[0]
Gives red on everything with error The field, constructor or member 'Item' is not defined.
The following "works"
((riot_js 1)?mount ("app", state))?``0``
And is compiled to:
riot_js.mount("app", this.state)["0"];
Not the best result someone can get. I'll let this question sit for a while and set a bounty on it for a week or so before opening 2 issues with Fable.

The following seems to compile to the right ES and does not need the ? so it'll be strongly typed.
open Fable.Core.JsInterop
type Riotjs =
{
mount:(System.Func<string,obj,string []>)
}
let riot = (importAll<obj> "../js/3rd/riot.js") :?> Riotjs
let app = riot.mount.Invoke("app",(createObj []))
I set initial state to type obj but can use a strong typed application state as well.
ES generated is:
export var app = riot.mount("app", {});

Related

Type Elmish.WPF main() function reports binding () -> #Window as an error

Why does my version of the Elmish.WPF Sample NewWindow (XAML code and F# Core) emit the error ...
The type 'unit -> 'a' is not compatible with the type 'Window'.
The same line emits the warning...
This construct causes code to be less generic than indicated by its type annotations. The type variable implied by the use of a '#', '_' or other type annotation at or near <line reference to createWindow_Window2 in the let bindings = statement">
Why am I getting this error and warning?
What I am doing is merging the Elmish.WPF Samples SingleCounter (XAML code and F# Core) and NewWindow (XAML code and F# Core) to have the Model, bindings(), and Msg parts in Program.fs instead of App.fs (as it was in the NewWindow sample).
My goal is to make a SimpleCounter able to open a NewWindow.
The XAML code passes Func<Window2> into the F# code here...
let main mainWindow (createWindow2: Func<#Window>)
...I define bindings as...
let bindings = Platform.bindings createWindow_Window2
I am down to the one compiler error I mentioned above (and that also appears in line beginning "let bindings = ..." below)...
let main mainWindow (createWindow_Window2: Func<#Window>) =
let logger =
LoggerConfiguration()
.MinimumLevel.Override("Elmish.WPF.Update", Events.LogEventLevel.Verbose)
.MinimumLevel.Override("Elmish.WPF.Bindings", Events.LogEventLevel.Verbose)
.MinimumLevel.Override("Elmish.WPF.Performance", Events.LogEventLevel.Verbose)
.WriteTo.Console()
.CreateLogger()
let createWindow_Window2 =
let window = createWindow_Window2.Invoke()
window.Owner <- mainWindow
window
let bindings = Platform.bindings createWindow_Window2
WpfProgram.mkProgramWithCmdMsg (fun _ -> m_init, []) update bindings toCmd
|> WpfProgram.withLogger (new SerilogLoggerFactory(logger))
|> WpfProgram.startElmishLoop mainWindow
The top of Program.bindings is defined as ...
let bindings (createWindow_Window2: unit -> #Window) () : Binding<Model, Msg> list = [
"Window_Window2_Show|> Binding.cmd Window_AboutProduct_Show
"Window_Window2" |> Binding.subModelWin(
Window_Window2.get >> WindowState.ofOption,
snd,
Window_Window2ct.mapInOutMsg,
Window_Window2_Module.Window_Window2.bindings,
createWindow_Window2,
isModal = true)
...bindings continue but are not relevant to this question...
... and reports no errors!
The compiler seems to think Platform.bindings is expecting unit -> `a when createWindow_Window2 is correctly typed as unit -> #Window.
You can see the call into Program.fs:main is correctly called from App.xaml.cs with a lambda function returning a Window2 as follows...
private void StartElmish(object sender, EventArgs e)
{
this.Activated -= StartElmish;
Program.main(MainWindow, () => new Window2());
}
So my question is why am I getting this mismatch type error when it seems I am passing the correct types?
Thank you!
I think one issue in your code is that the name createWindow_Window2 is used both as the name of an argument of main, but then later also as a local variable in the function of differnt type. Another issue is that in one place, you try to use a delegate type Func<#Window> but in another place, you have an ordinary F# function Window -> unit.
My advice would be to use Window -> unit everywhere and avoid variable shadowing:
let main mainWindow (createWindow_Window2:Window -> unit) = (* Changed type here *)
let logger = (* omitted *)
let createWindowWithOwner () =
(* Renamed and added '()' so that it is a function *)
let window = createWindow_Window2 () (* Just function call *)
window.Owner <- mainWindow
window
let bindings =
Platform.bindings createWindowWithOwner (* Pass the right function here *)
(* omitted *)
A big thank you to: Brian Berns, Bent Tranberg, and Tomas Petricek for helping me!
The answer to my problem was astonishingly simple to an expert but to a relative novice only yielded after meticulous comparison with the working Elmish.WPF code for NewWindow.
For easy comparison I have pushed up to rfreytag/Elmish.WPF a version of the NewWindow (XAML code and F# Core) sample that compiles and runs (REMEMBER to build the NewWindow sample).
This version of NewWindow (XAML code and F# Core) when compiled shows the error and warning I reported above …
The type 'unit -> 'a' is not compatible with the type 'Window'.
See also C:\Workspace\Elmish.WPF\src\Samples\AnotherNewWindow.Core\Program.fs(78,72)-(78,79).
...and the warning...
This construct causes code to be less generic than indicated by its
type annotations. The type variable implied by the use of a '#', '_'
or other type annotation at or near
'C:\Workspace\Elmish.WPF\src\Samples\AnotherNewWindow.Core\Program.fs(78,72)-(78,79)'
has been constrained to be type 'unit -> 'a'.
To see the precise fix you can compare the working and breaking branches on my copy of Elmish.WPF. Which is that I had forgotten the () following the definition of let createWindow_Window2 = ...
let createWindow_Window2 =
let window = createWindow_Window2.Invoke()
window.Owner <- mainWindow
window
... returns the function () -> Window while the correct ...
let createWindow_Window2 () =
let window = createWindow_Window2.Invoke()
window.Owner <- mainWindow
window
... returns the needed Window.
A newbie looks at unit → ‘a and doesn’t immediately recognize it as a function. And of course, functions are not C# Window objects.
Did not help me that the error message doesn’t follow the F# form of...
expecting a <type sought by context>
but was given a <passed in type>
...which has become familiar from working with the F# compiler.
Not sure why this message diverged from that pattern. Maybe someone can explain that?
The warning is alerting to the less-specific #Window (see # ‘flexible type’ definition) possibly clashing at run-time with the unit → ‘a function.
As usual, learning a new framework means learning the compiler and linker messages.

F# non-static methods in modules

I am an absolute newbie to coding, but I need to modify a F# script. It always gives me the error "Method or object constructor 'x' is not static". I read that this might be due to the fact that I try to call a non-static method within a module, which is by default static. For example 'x' = Get.Axis():
module Primitives =
let axis1 = Zaber.Motion.Ascii.Device.GetAxis(1)
The manual only provides code in C#: var axis1 = device.GetAxis(1);
If I use static member instead of let, I'll get a 'unexpected keyword static in definition' error, although I checked the indentation as suggested in another question.
Assuming you're using the Zaber Motion Library, I think what you need to do is get an instance of a device first, instead of trying to access the class in a static context.
Their documentation includes an example of how to get a list of devices by opening a serial port:
open Zaber.Motion.Ascii
use connection = Connection.OpenSerialPort("COM3")
let deviceList = connection.DetectDevices()
match deviceList |> Seq.tryHead with // See if we got at least one device
| Some device ->
let axis = device.GetAxis(1)
// TODO: Do whatever you want with the axis here
| None ->
failwith "No Devices Found on COM3"

Can I parse some F# code at run-time that reference types in my current assembly?

Say I have the following type defined:
type Foo = { A: string; B: int }
I want a function parse, such that:
let myfoo = parse<Foo> "{A = \"foo\"; B = 5}"
gives me an instance of type Foo (or error).
Is this possible using FSharp.Compiler.Service?
UPDATE:
While there are other questions that address parsing of F# code, they don't address having references in the current assembly.
You can do this by referencing the current assembly from the hosted F# interactive - this only works if you are running this from a compiled program (which has assembly located on disk) and if your types are public, but it may do the trick in your case.
Given the usual setup documented on the Embedding F# Interactive page, you can do something like this:
module Program
type Test = { A:int; B:string }
// (omitted code to initialize the fsi service)
let fsiSession = FsiEvaluationSession.Create(...)
// Run #r command to reference the current assembly
let loc = System.Reflection.Assembly.GetExecutingAssembly().Location
fsiSession.EvalInteraction(sprintf "#r #\"%s\"" loc)
// Open the module or namespace containing your types
fsiSession.EvalInteraction("open Program")
// Evaluate code using the type and cast it back to our type
let value = fsiSession.EvalExpression("{A=0; B=\"hi\"}").Value.ReflectionValue :?> Test
printfn "%A" value

F#, the value or constructor 'List' is not defined

I am copying this code out of a book on F#. The compiler is supposed to create a generic function.
Instead, I am getting an error
"FS0039: The value or constructor 'List' is not defined."
Why?
let createList(first, second) =
let output = List()
output.Add(first)
output.Add(second)
output
// expected output below
// val createList : first:'a * second:'a -> List<'a>
I added this line to the script
open System.Collections.Generic
unlike C#, Visual Studio F# isn't good at telling you when you need to add an open statement like this.

F# How to use an interface in a separate module

So I have been doing research on interfaces on F#. I have found these 2 articles on it. The MSDN and F# for fun and profit But unfortunately they are only skin deep.
UPDATED
here is my module with my interfaces
//open statements omitted for brevity
module DrawingInterfaces =
///gets a string representation of the SVG code representation of the object
type IRepresentable_SVG =
abstract member getSVGRepresenation : unit -> string
//other interfaces omitted for brevity
Now within the same namespace and physical folder also I have this:
type lineSet (x1off,x2off,y1off,y2off,x1,x2,y1,y2,rot,rotOff,count) =
//tons of member vals omitted for brevity
member val x1Start = x1 with get, set
interface DrawingInterfaces.IRepresentable_SVG with
member __.getSVGRepresenation() =
let mutable svg = ""
let mutable currentx1 = x1Start
svg
This used to give me 2 errors, before I was using the __. notation for the member. The first error was on the interface line. And a second on the member line.
The errors were respectively:
The type 'IRepresentable_SVG' is not defined
This instance member needs a parameter to represent the object being invoked.
I was able to fix the first one by changing the file order. Thanks to John Palmer.
The second one is nearly fixed./
After using the __ . notation I was able to get rid of the second error. However, now a new error pops up when I try to use type members in my interface implementation.
let mutable currentx1 = x1Start
x1Start shows as not being defined. I need to be able to use values stored in my other members within my implementation.
Let's first make it work and then point to your problems. I define below 2 separate modules in 2 separate .fs files within the same namespace Example for interface definition in module Example.DrawingInterfacesand interface implementation in module Example.UseInterface and also a console app that will use the interface from third (implicit) module Program. In my project correspondent code files are in the following order: DefInterface.fs, UseInterface,fs, Program.fs (I also made few idiomatic styling changes and more brevity omissions)
File: DefInterface.fs
namespace Example
module DrawingInterfaces =
type IRepresentable_SVG =
abstract member GetSVGRepresenation : unit -> string
File: UseInterface.fs
namespace Example
module UseInterface =
type LineSet (x1) =
member val X1Start = x1 with get, set
interface DrawingInterfaces.IRepresentable_SVG with
member __.GetSVGRepresenation() = "test" + " " + __.X1Start.ToString()
File: Program.fs
open Example
open System
[<EntryPoint>]
let main argv =
let lineSet = UseInterface.LineSet(5)
let example : DrawingInterfaces.IRepresentable_SVG = lineSet :> _
example.GetSVGRepresenation() |> printfn "%A"
lineSet.X1Start <- 10
example.GetSVGRepresenation() |> printfn "%A"
0
Compile, run and make sure it works.
Now to problems in your code:
first error message stems from the need to refer to the full implemented interface name in UseInterface.fs, which is Example.DrawingInterfaces.IRepresentable_SVG although as both modules belong to the same namespace the Example prefix may be omitted
second error message points to the need of using instance method in implementation class UseInterface.LineSet, which is achieved by prepending self-identifier __. to the method signature
Finally, notice the usage of your interface in Program.fs that imports namespace, provides module names for definition and implementation respectively, and also explicitly casts implementation LineSet to IRepresentable_SVG.
EDIT: I've added X1Start property to the original LineSet to show how it can be used from interface implementation per question author's request. Now self-id __. is more involved and probably using self. or even this. instead would make more sense.

Resources