Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Don't merge: Compile with Dart #241

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Fable.Elmish.fsproj
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DefineConstants>$(DefineConstants);FABLE_COMPILER</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="prelude.fs" />
<Compile Include="cmd.fs" />
<Compile Include="cmd.obsolete.fs" />
<!-- <Compile Include="cmd.obsolete.fs" /> -->
<Compile Include="ring.fs" />
<Compile Include="program.fs" />
</ItemGroup>
<ItemGroup>
<Content Include="*.fsproj; *.fs" PackagePath="fable\" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.*" />
<!-- <PackageReference Update="FSharp.Core" Version="4.7.*" /> -->
<PackageReference Include="Fable.Core" Version="3.*" />
</ItemGroup>
</Project>
3 changes: 2 additions & 1 deletion src/cmd.fs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ module Cmd =
x |> (ofError >> dispatch)
[bind]

(*
module OfAsyncWith =
/// Command that will evaluate an async block and map the result
/// into success or error (of exception)
Expand Down Expand Up @@ -220,7 +221,7 @@ module Cmd =
(ofError: _ -> 'msg) : Cmd<'msg> =
OfAsync.attempt (task >> Async.AwaitTask) arg ofError
#endif

*)
/// Command to issue a specific message
let inline ofMsg (msg:'msg) : Cmd<'msg> =
[fun dispatch -> dispatch msg]
Expand Down
29 changes: 16 additions & 13 deletions src/prelude.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ Basic cross-platform logging API.
*)
module internal Log =

#if FABLE_COMPILER
open Fable.Core.JS
#if FABLE_COMPILER_DART
open Fable.Core

let onError (text: string, ex: exn) = console.error (text,ex)
let toConsole(text: string, o: #obj) = console.log(text,o)
[<Global>]
let print (info: obj): unit = ()

let onError (text: string, ex: exn) = print $"{text} {ex}"
let toConsole(text: string, o: #obj) = print $"{text} {o}"

#else
#if NETSTANDARD2_0
Expand All @@ -24,12 +27,12 @@ module internal Log =
#endif
#endif

#if FABLE_COMPILER
module internal Timer =
open System.Timers
let delay interval callback =
let t = new Timer(float interval, AutoReset = false)
t.Elapsed.Add callback
t.Enabled <- true
t.Start()
#endif
// #if FABLE_COMPILER
// module internal Timer =
// open System.Timers
// let delay interval callback =
// let t = new Timer(float interval, AutoReset = false)
// t.Elapsed.Add callback
// t.Enabled <- true
// t.Start()
// #endif
12 changes: 8 additions & 4 deletions src/program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ module Program =
let mutable reentered = false
let mutable state = model
let mutable terminated = false
let rec dispatch msg =

// TODO: LetRec in Dart
let mutable dispatch' = Unchecked.defaultof<_>
let dispatch msg =
if terminated then ()
else
if reentered then
Expand All @@ -170,11 +173,12 @@ module Program =
else
let (model',cmd') = program.update msg state
program.setState model' dispatch'
cmd' |> Cmd.exec (fun ex -> program.onError (sprintf "Error handling the message: %A" msg, ex)) dispatch'
cmd' |> Cmd.exec (fun ex -> program.onError ($"Error handling the message: {msg}", ex)) dispatch'
state <- model'
nextMsg <- rb.Pop()
reentered <- false
and dispatch' = syncDispatch dispatch // serialized dispatch

dispatch' <- syncDispatch dispatch // serialized dispatch

program.setState model dispatch'
let sub =
Expand All @@ -184,7 +188,7 @@ module Program =
program.onError ("Unable to subscribe:", ex)
Cmd.none
Cmd.batch [sub; cmd]
|> Cmd.exec (fun ex -> program.onError (sprintf "Error intitializing:", ex)) dispatch'
|> Cmd.exec (fun ex -> program.onError ("Error intitializing:", ex)) dispatch'


/// Start the single-threaded dispatch loop.
Expand Down
14 changes: 7 additions & 7 deletions src/ring.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ open System

[<Struct>]
type internal RingState<'item> =
| Writable of wx:'item array * ix:int
| ReadWritable of rw:'item array * wix:int * rix:int
| Writable of wx:'item option array * ix:int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the impulse to optionize, but the array is internal impl detail and there's nothing wrong with it using unchecked default for values because index range is tightly controlled anyway. From performance perspective having an option instead of value doubles the number of allocations and GC pressure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is a tricky thing with the Dart implementation right now. Dart has null safety so if I try to assign null to a generic it will complain and there's no such a thing as default(T) so I haven't found a way to allocate an array without providing a fill value.

At the moment, I'm compiling options as nullables in Dart so by using 'item option I can assign null as default value. But I understand this has implications for .NET (would OptionValue help?) so I'm not asking to change it. Let's see if I manage to solve the issue in Dart, if not maybe we can also use #if FABLE_COMPILER here, although I'm also trying to avoid proliferation of conditional compilation :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, voption should work and I think would be more or less free from perf/cost perspective.

| ReadWritable of rw:'item option array * wix:int * rix:int

type internal RingBuffer<'item>(size) =
let doubleSize ix (items: 'item array) =
let doubleSize ix (items: 'item option array) =
seq { yield! items |> Seq.skip ix
yield! items |> Seq.take ix
for _ in 0..items.Length do
yield Unchecked.defaultof<'item> }
yield None }
|> Array.ofSeq

let mutable state : 'item RingState =
Expand All @@ -26,18 +26,18 @@ type internal RingBuffer<'item>(size) =
state <- Writable(items, wix)
| _ ->
state <- ReadWritable(items, wix, rix')
Some items.[rix]
items.[rix]
| _ ->
None

member __.Push (item:'item) =
match state with
| Writable (items, ix) ->
items.[ix] <- item
items.[ix] <- Some item
let wix = (ix + 1) % items.Length
state <- ReadWritable(items, wix, ix)
| ReadWritable (items, wix, rix) ->
items.[wix] <- item
items.[wix] <- Some item
let wix' = (wix + 1) % items.Length
match wix' = rix with
| true ->
Expand Down