| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
import van from "vanjs-core"
const { a, p, div, li, ul } = van.tags
// Reusable components can be just pure vanilla JavaScript functions.
// Here we capitalize the first letter to follow React conventions.
const Hello =
() =>
div(
p("Hello"),
ul(
li("?️World"),
li(a({ href: "https://vanjs.org/" }, "?VanJS")),
),
)
van.add(document.body, Hello())Try on jsfiddle
module HelloApp
open Browser
open Browser.Types
open Fable.Core.JsInterop
open Van.Basic // import tags, add
let a: Tag = tags?a
let p: Tag = tags?p
let div: Tag = tags?div
let ul: Tag = tags?ul
let li: Tag = tags?li
let Hello =
fun _ ->
div [
p ["Hello"]
ul [
li ["?️World"]
li [a [{|href="https://vanjs.org/"|}; "?VanJS"]]
]
]
add [document.body; Hello()]
|> ignoreDemo
https://codepen.io/kentechgeek/pen/VwNOVOx
let Greeting: Tag =
fun list ->
let name: string = list[0]?name
div [$"Hello {name}!"]
add [document.body; Greeting [{|name="Ken"|}]]
|> ignoreconst Greeting: Component<{ name: string }> =
({ name }) =>
<div>Hello {name}!</div>;
render(() => <Greeting name="Ken" />, document.body);VanFS project includes some TypeScript code.
https://github.com/ken-okabe/vanfs/blob/main/van-api/ts/basic.ts
TS code for the purpose of conversion using JS Proxy:
// unary function ([a,b,c,...]) in F#
// -> n-ary function (a,b,c,...) in VanJSThis is under the van-api directory which is essential and we would not want to modify it to keep things working.
Users must install any required CSS or Web Components .
VanJS does not provide the specific installation support beause it's just a VanillaJS.
On the other hand, VanFS clarifies the step-by-step process as below:
Everything we need to customize or import is located under web-imports directory.
import {
provideFluentDesignSystem,
fluentCard,
fluentCheckbox
} from "@fluentui/web-components";
provideFluentDesignSystem()
.register(
fluentCard()
);
provideFluentDesignSystem()
.register(
fluentCheckbox()
);export let cssURLs = [
"https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200"
];Regardless, all the required code within the VanFS project is compiled into a single VanillaJS bundle using Fable and Vite.
let bindT = <A, B>
(monadf: (a: A) => Timeline<B>) =>
(timelineA: Timeline<A>): Timeline<B> => {
let timelineB = monadf(timelineA.lastVal);
let newFn = (a: A) => {
nextT(monadf(a).lastVal)(timelineB);
return undefined;
};
timelineA.lastFns = timelineA.lastFns.concat([newFn]);
return timelineB;
};In TypeScript, compared with legacy JavaScript, an additional step is required to add type signatures to all variables, functions, and parameters. This is often overwhelming.
let bindT =
fun monadf timelineA ->
let timelineB = timelineA.lastVal |> monadf
let newFn =
fun a ->
timelineB
|> nextT (a |> monadf).lastVal
|> ignore
timelineA.lastFns <- timelineA.lastFns @ [ newFn ]
timelineBThe F# code is much cleaner and more readable than TypeScript code.
In F#, we rarely need to add types manually thanks to its powerful type inference. This makes F# development feel similar to legacy JavaScript coding.
In reality, it is much more than that.
While programmers may want to define fundamental object types that form the backbone of their code, in other places, if the F# compiler warns for a demand of manual type annotations, usually, something is wrong .
In F#, if the compiler cannot infer the type, it often suggests that there may be mathematical inconsistencies.
In TypeScript, if the compiler cannot infer the type, it often suggests limitations in its type inference capabilities. This makes it hard to determine the precise cause of the problem.
As a result, F# programmers are naturally led to write mathematically consistent and rigorous code; unfortunately, this benefit rarely happens in TypeScript.
F# is generally recognized as running on the .NET Framework, but just as TypeScript is compiled to JavaScript, F# is also compiled to JavaScript.
TypeScript -> JavaScript
F# -> JavaScript
More precisely,
TypeScirpt
⬇ TypeScript Compiler running on Node.js (npx tsc)
JavaScript running in the browser
F#
⬇ Fable Compiler running on .NET (dotnet fable)
JavaScript running in the browser
Therefore, the backbone of VanFS is Fable.
| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
.NET SDK
Node.js and npm CLI or Alternatives (Bun / Deno / yarn etc.)
If you are new to F# and using VSCode, read F# Settings on VSCode.
A VanFS/Fable project is a hybrid of F#.NET project and npm project .
See Fable Setup Documentaion
git clone https://github.com/ken-okabe/vanfs
cd vanfs
dotnet restore # .NET project setup
dotnet tool restore
npm i # npm project setupbody {
font-family: sans-serif;
padding: 1em;
background-color: beige;
}Demo
https://codepen.io/kentechgeek/pen/zYXQyxz
| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
VanFS can leverage custom HTML tags provided by Web Components with design systems : Microsoft Fluent, Google Material Design, etc. .
import {
provideFluentDesignSystem,
fluentCard,
fluentCheckbox
} from "@fluentui/web-components";
provideFluentDesignSystem()
.register(
fluentCard()
);
provideFluentDesignSystem()
.register(
fluentCheckbox()
);body {
font-family: sans-serif;
padding: 1em;
background-color: beige;
}
.custom {
--card-width: 200px;
--card-height: 150px;
padding: 22px;
}Program.fsmodule WebComponentsApp
open Browser
open Browser.Types
open Fable.Core.JsInterop
open Van.Basic // import tags, add
let br : Tag = tags?br
// Define the fluent-card and fluent-checkbox tags
let fluentCard: Tag = tags?``fluent-card``
let fluentCheckbox: Tag = tags?``fluent-checkbox``
let List =
fun _ ->
fluentCard [
{|``class``="custom"|}
// class is a reserved word in F#
// so we use backticks to escape it
fluentCheckbox ["Did you check this?"]
br []
fluentCheckbox [{|``checked``=true; disabled=true|}; "Is this disabled?"]
br []
fluentCheckbox [{|``checked``=true|}; "Checked by default?" ]
]
add [document.body; List()]
|> ignoreWhen major changes are made, cleaning the Fable project is sometimes necessary.
dotnet fable clean
dotnet fable watchnpx viteimport '@material/web/textfield/filled-text-field.js';
import '@material/web/button/text-button.js';
import '@material/web/button/outlined-button.js';.custom3 {
--card-width: 460px;
--card-height: 150px;
padding: 20px;
}
.row {
align-items: flex-start;
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.buttons {
justify-content: flex-end;
padding: 16px;
}
md-filled-text-field,
md-outlined-text-field {
width: 200px;
}module MaterialUI
open Browser
open Browser.Types
open Fable.Core.JsInterop
open Van.Basic // import tags, add
let div: Tag = tags?div
let form: Tag = tags?form
let fluentCard: Tag = tags?``fluent-card``
let mdFilledTextField: Tag = tags?``md-filled-text-field``
let mdTextButton: Tag = tags?``md-text-button``
let mdOutlinedButton: Tag = tags?``md-outlined-button``
let Form =
fun _ ->
fluentCard [
{|``class``="custom3"|}
form [
div [
{|``class``="row"|}
mdFilledTextField [
{|
label="First name"
name="first-name"
required=""
autocomplete="given-name"
|}
]
mdFilledTextField [
{|
label="Last name"
name="last-name"
required=""
autocomplete="family-name"
|}
]
]
div [
{|``class``="row buttons"|}
mdTextButton [
{|``type``= "reset"|}
"Reset"
]
mdOutlinedButton [
{|``type``= "submit"|}
"Submit"
]
]
]
]
add [document.body; Form()]
|> ignorenpx vite buildDemo
https://codepen.io/kentechgeek/pen/KKYLwgN?editors=1111
import '@material/web/icon/icon.js';
import '@material/web/iconbutton/icon-button.js';https://m3.material.io/styles/icons/overview
export let cssURLs = [
"https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200"
];| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
VanFS is described as
1:1 bindings from F# to VanJS (A tiny Reactive UI Framework without React/JSX) + WebComponents + micro FRP
or
VanFS is a F# project template for one-to-one direct bindings of VanJS
1:1 bindings is absolutely true within the scope of the basic features for composing UIs, but not a case for its state management.
VanJS reactively binds its state objects to corresponding DOM elements. This means that when a state object updates, the corresponding DOM element automatically updates as well. This approach is a common feature among declarative UI libraries such as React, SolidJS, etc.
This is the identical structure of:
So, this is FRP.
Functional Reactive Programming (FRP) is a programming paradigm that uses mathematical expressions, specifically binary operations , as a means of implementing Reactive Programming .
| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
Timeline is a fundamentally standalone FRP library, with no dependencies on VanJS or F# asynchronous features. The codebase is a compact pure function implementation of approximately 30-40 lines of code.
Timeline<'a>record Timeline<'a>
val mutable lastVal: 'a
val el: StateElement<'a>| Field | Description | Van.state |
|---|---|---|
lastVal |
Last value of the Timeline | State.val |
el |
Reactive DOM element of the Timeline | State |
Timeline<'a>Timeline'a -> Timeline<'a>let counter = van.state(0);
console.log(counter.val);
// 0let counter = Timeline 0
console.log counter.lastVal
// 0Consider the Timeline as a specific container for a value, similar to a Cell in spreadsheet apps.
let double = fun a -> a * 2
let timelineA = Timeline 1
let timelineB =
timelineA |> mapT double
console.log timelineB.lastVal
// 2This code for the binary operation simply corresponds to the basic usage of spreadsheet apps
This is the identical structure of:
let double = a => a * 2;
let arrayA = [1];
let arrayB =
arrayA.map(double);
console.log(arrayB);
// [2]let double =
fun a -> a * 2
let listA = [1]
let listB =
listA |> List.map double
console.log listB
// [2]We could recognize the array [2] is identical to the Cell and Value 2 of a spreadsheet; however, the spreadsheet and Timeline maintain a double relationship as values change over the timeline .
Timeline<'a>nextT'a -> Timeline<'a> -> Timeline<'a>let timelineA' =
timelineA |> nextT 3or, in most cases, we don’t need another timelineA' and want to discard it, so simply ignore the returned value.
let timelineA = Timeline 1
timelineA
|> nextT 3
|> ignore
console.log timelineA.lastVal
// 3let double = fun a -> a * 2
// ① initialize timelineA
let timelineA = Timeline 1
// confirm the lastVal of timelineA
console.log timelineA.lastVal
// 1
// ② the binary operation
let timelineB =
timelineA |> mapT double
// confirm the lastVal of timelineB
console.log timelineB.lastVal
// 2
//=====================================
// ③ update the lastVal of timelineA
timelineA
|> nextT 3
|> ignore
// update to timelineA will trigger
// a reactive update of timelineB
// confirm the lastVal of timelineA & timelineB
console.log timelineA.lastVal
// 3
console.log timelineB.lastVal
// 6import van from "vanjs-core"
const { button, div, h2 } = van.tags
const Counter =
() => {
const counter = van.state(0)
van.derive(() =>
console.log(`Counter: ${counter.val}`))
return div(
h2("❤️ ", counter),
button(
{
onclick: () => ++counter.val
},
"?"
),
button(
{
onclick: () => --counter.val
},
"?"
),
)
}
van.add(document.body, Counter())module CounterApp
open Browser
open Browser.Types
open Fable.Core.JsInterop
open Van.Basic // import tags, add
open Van.TimelineElement // import Timeline
let div: Tag = tags?div
let h2: Tag = tags?h2
let icon: Tag = tags?``md-icon``
let iconButton: Tag = tags?``md-icon-button``
let Counter =
fun _ ->
let counter = Timeline 0 // ① initialize an Timeline
counter // ② the binary operation of the Timeline
|> mapT (fun value ->
console.log $"Counter: {value}")
|> ignore
// ignore the return value of `console.log`
div [
h2 ["❤️ "; counter.el] // ? `counter.el`
iconButton [ // for Reactive DOM element
{|onclick = fun _ ->
counter // ③ update the Timeline
|> nextT (counter.lastVal + 1)
|}
icon ["thumb_up"]
]
iconButton [
{|onclick = fun _ ->
counter // ③ update the Timeline
|> nextT (counter.lastVal - 1)
|}
icon ["thumb_down"]
]
]
add [document.body; Counter()]
|> ignoreDemo
https://codepen.io/kentechgeek/pen/gOyqNqb?editors=1111
| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
Given the critical significance of Null in modern software development, I have dedicated a separate article to exploring its key concepts and benefits.
Nullable value types in F#
A nullable value type
Nullable<'T>represents any struct type that could also benull. This is helpful when interacting with libraries and components that may choose to represent these kinds of types, like integers, with anullvalue for efficiency reasons. The underlying type that backs this construct is System.Nullable.
can only represents struct type, which limitation is problematic.
Using Nullable Reference Types in F#
F#: How do I convert Option<'a> to Nullable, also when 'a can be System.String?
It would be nice if we could write any Nullable types including reference types in F#.
F# RFC FS-1060 - Nullable Reference Types
let nullable1 =
Null
let nullable2 =
NullableT "hello"
log nullable1
// Null
log nullable2
// T hello
log nullable2.Value
// helloThis specification resembles F#'s native nullable value types, but unlike it, NullableT can also represent any reference types.
F# Nullness support may come soon!
a preview of the work being done on the F# compiler to support .NET's nullness capabilities.
| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
By utilizing the Nullable type , we can provide new operators that pair with Timeline .
Initializing a Timeline with Null value, the provided function remains unexecuted and waits in a pending state. Once the Timeline value is updated to a non Null value by a valid event, the function is then triggered and executed.
Timeline<NullableT<'a>>TimelineNullableT<'a> -> Timeline<NullableT<'a>>let timelineNullable = Timeline Null
log timelineNullable.lastVal // use `log` of Timeline
// NullConsider this Timeline as an empty Cell in spreadsheet apps.
Timeline type and the function:
① Function to initialize Timeline<'a>
① Function to initialize Timeline<NullableT<'a>>
is the same entity .
Consider Timeline can accept any generic types of 'a including NullableT<'a>.
On the other hand, in the case of Timeline<NullableT<'a>> where the parameter value is a nullable type, if we need the Timeline behavior to ignore the provided function and simply pass through the Null value when the parameter is Null , we can use specific operators as shown below.
mapTN(NullableT<'a> -> NullableT<'b>) -> (Timeline<NullableT<'a>> -> Timeline<NullableT<'b>>)bindTN(NullableT<'a> -> Timeline<NullableT<'b>>) -> (Timeline<NullableT<'a>> -> Timeline<NullableT<'b>>)When the binary operator: mapT,
let double =
fun a -> NullableT (a * 2)
// ① initialize timelineA
let timelineA = Timeline Null
log timelineA.lastVal // use `log` of Timeline
// Null
// ② the binary operation
let timelineB =
timelineA |> mapTN double
// currently, `timelineA = Timeline Null`
// so, `double` function is ignored
// and `timelineB` value becomes `Null`
log timelineB.lastVal // use `log` of Timeline
// NullThis code for the binary operation simply corresponds to the basic usage of spreadsheet apps.
let timelineA' =
timelineA |> nextTN (NullableT 3)or, in most cases, we don’t need another timelineA' and want to discard it, so simply ignore the returned value.
Timeline<'a>let double =
fun a -> NullableT (a * 2)
// ① initialize timelineA
let timelineA = Timeline Null
log timelineA.lastVal // use `log` of Timeline
// Null
// ② the binary operation
let timelineB =
timelineA |> mapTN double
// currently, `timelineA = Timeline Null`
// so, `double` function is ignored
// and `timelineB` value becomes `Null`
log timelineB.lastVal // use `log` of Timeline
// Null
// ③ update the lastVal of timelineA
timelineA
|> nextTN (NullableT 3)
|> ignore
log timelineA.lastVal // use `log` of Timeline
// T 3
// Now, `timelineA` value is updated to non `Null` value
// Accordingly, `timelineB` reactively becomes `double` of it
log timelineB.lastVal
// T 6module Number
open Browser
open Browser.Types
open Fable.Core.JsInterop
open Van.Basic // import tags, add
open Van.TimelineElement // import Timeline
open Van.TimelineElementNullable // import Timelinetc.
open Van.Nullable // import NullableT
open Van.TimelineElementTask
let h4: Tag = tags?h4
let fluentCard: Tag = tags?``fluent-card``
let fluentTextField: Tag = tags?``fluent-text-field``
let Number =
fun _ ->
let number = Timeline Null
let numberX2 =
number
|> mapTN (fun n -> NullableT(n * 2)) //System.Nullable
fluentCard [
{|``class``="custom1"|}
h4 [ "Number" ]
fluentTextField [
{|
appearance="outline"
required=true
``type``="number"
placeholder="1"
oninput=
fun e ->
let value =
if e?target?value=""
then Null
else NullableT e?target?value
if value=Null // clear the output textField
then numberX2
|> nextTN Null
|> ignore
document.getElementById("output-field")?value
<- "Null" // clear the output textField
else ()
number
|> nextTN value
|> ignore
|}
]
h4 [ "Number × 2" ]
fluentTextField [
{|
appearance="outline"
readonly=true
value=numberX2.el
id="output-field"
|}
]
]
add [document.body; Number()]
|> ignoreDemo
https://codepen.io/kentechgeek/pen/wvZNVzJ?editors=1111
| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
While Timeline Nullable operators offer a basic principle similar to JavaScript's Promise, they are not capable of managing Task chaining, such as Promie.then.
Based on Timeline Nullable , we can obtain Timeline Task which is capable of Task chaining.
TaskTimeline<NullableT<'a>> -> 'b -> unit let task =
fun timelineResult previousResult ->
log "-----------task1 started..."
log previousResult
// delay-------------------------------
let f = fun _ ->
log ".......task1 done"
timelineResult
|> nextTN (NullableT 1)
|> ignore
setTimeout f 2000taskTTask<'a, NullableT<'a0>> -> Timeline<NullableT<'a>> -> Timeline<NullableT<'a>>let timelineStarter =
Timeline (NullableT 0)
// tasks start immediately
timelineStarter
|> taskT task1
|> taskT task2
|> taskT task3
|> ignore| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
taskConcat or (+>)(Task -> Task) -> Tasklet task12 =
task1 +> task2
let task123 =
task1 +> task2 +> task3
let task1234 =
task123 +> task4module Tasks
open Browser
open Browser.Types
open Fable.Core.JsInterop
open Van.Basic // import tags, add
open Van.TimelineElement // import Timeline
open Van.TimelineElementNullable // import Timelinetc.
open Van.Nullable // import NullableT
open Van.TimelineElementTask
open Van.TimelineElementTaskConcat
open System.Timers
let setTimeout f delay =
let timer = new Timer(float delay)
timer.AutoReset <- false
timer.Elapsed.Add(fun _ -> f())
timer.Start()
let br: Tag = tags?``br``
let fluentCard: Tag = tags?``fluent-card``
let linerProgress: Tag = tags?``md-linear-progress``
let Tasks =
fun _ ->
let progressInit = false
let progressStart = true
let progressDone = false
let percentInit = 0.0
let percentStart = 0.0
let percentDone = 1.0
let timelineProgress1 = Timeline progressInit
let timelineProgress2 = Timeline progressInit
let timelineProgress3 = Timeline progressInit
let timelinePercent1 = Timeline percentInit
let timelinePercent2 = Timeline percentInit
let timelinePercent3 = Timeline percentInit
let taskStart =
fun timelineProgress timelinePercent ->
timelineProgress
|> nextT progressStart
|> ignore
timelinePercent
|> nextT percentStart
|> ignore
let taskDone =
fun timelineProgress timelinePercent timelineResult->
timelineProgress
|> nextT progressDone
|> ignore
timelinePercent
|> nextT percentDone
|> ignore
timelineResult
|> nextTN (NullableT 999)
|> ignore
let task1 =
fun timelineResult previousResult ->
log "-----------task1 started..."
taskStart timelineProgress1 timelinePercent1
// delay-------------------------------
let f = fun _ ->
log "...task1 done"
taskDone timelineProgress1 timelinePercent1 timelineResult
setTimeout f 2500
let task2 =
fun timelineResult previousResult ->
log "-----------task2 started..."
taskStart timelineProgress2 timelinePercent2
// delay-------------------------------
let f = fun _ ->
log "...task2 done"
taskDone timelineProgress2 timelinePercent2 timelineResult
setTimeout f 2500
let task3 =
fun timelineResult previousResult ->
log "-----------task3 started..."
taskStart timelineProgress3 timelinePercent3
// delay-------------------------------
let f = fun _ ->
log "...task3 done"
taskDone timelineProgress3 timelinePercent3 timelineResult
setTimeout f 2500
let timelineStarter = Timeline Null //tasks disabled initially
let task123 =
task1 +>
task2 +>
task3
timelineStarter
|> taskT task123
|> ignore
(* task123 can be written as below
timelineStarter
|> taskT task1
|> taskT task2
|> taskT task3
|> ignore
*)
let start =
fun _ -> // timeline will start
timelineStarter
|> nextTN (NullableT 0)
|> ignore
setTimeout start 2000
fluentCard [
{|``class``="custom2"|}
br []
linerProgress [
{|indeterminate=timelineProgress1.el
value=timelinePercent1.el|}
]
br []
linerProgress [
{|indeterminate=timelineProgress2.el
value=timelinePercent2.el|}
]
br []
linerProgress [
{|indeterminate=timelineProgress3.el
value=timelinePercent3.el|}
]
]
add [document.body; Tasks()]
|> ignoreDemo
https://codepen.io/kentechgeek/pen/jORdjvy?editors=1111
module Tasks
open Van.TimelineElement // import Timeline
open Van.TimelineElementNullable // import Timelinetc.
open Van.Nullable // import NullableT
open Van.TimelineElementTask
open Van.TimelineElementTaskConcat
open System.Timers
let setTimeout f delay =
let timer = new Timer(float delay)
timer.AutoReset <- false
timer.Elapsed.Add(fun _ -> f())
timer.Start()
let nonNull = NullableT true // some non-null value
let task1 =
fun timelineResult previousResult ->
log "-----------task1 started..."
// delay-------------------------------
let f = fun _ ->
log "...task1 done"
timelineResult
|> nextTN nonNull
|> ignore
setTimeout f 2500
let task2 =
fun timelineResult previousResult ->
log "-----------task2 started..."
// delay-------------------------------
let f = fun _ ->
log "...task2 done"
timelineResult
|> nextTN nonNull
|> ignore
setTimeout f 1000
let task3 =
fun timelineResult previousResult ->
log "-----------task3 started..."
// delay-------------------------------
let f = fun _ ->
log "...task3 done"
timelineResult
|> nextTN nonNull
|> ignore
setTimeout f 3000
let timelineStarter = Timeline Null //tasks disabled initially
let task123 =
task1 +>
task2 +>
task3
timelineStarter
|> taskT task123
|> ignore
(* task123 can be written as below
timelineStarter
|> taskT task1
|> taskT task2
|> taskT task3
|> ignore
*)
let start =
fun _ -> // timeline will start
timelineStarter
|> nextTN nonNull
|> ignore
setTimeout start 2000
Demo
https://codepen.io/kentechgeek/pen/BaEeyvL?editors=1111
| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
taskOr or (+|)(Task -> Task) -> Tasklet task12 =
task1 +| task2
let task123 =
task1 +| task2 +| task3
let task1234 =
task123 +| task4module TaskOr
open Van.TimelineElement // import Timeline
open Van.TimelineElementNullable // import Timelinetc.
open Van.Nullable // import NullableT
open Van.TimelineElementTask
open Van.TimelineElementTaskOr
open System.Timers
let setTimeout f delay =
let timer = new Timer(float delay)
timer.AutoReset <- false
timer.Elapsed.Add(fun _ -> f())
timer.Start()
let nonNull = NullableT true
let task1 =
fun timelineResult previousResult ->
log "-----------task1 started..."
// delay-------------------------------
let f = fun _ ->
log "...task1 done"
timelineResult
|> nextTN (NullableT "task1")
|> ignore
setTimeout f 2500
let task2 =
fun timelineResult previousResult ->
log "-----------task2 started..."
// delay-------------------------------
let f = fun _ ->
log "...task2 done"
timelineResult
|> nextTN (NullableT "task2")
|> ignore
setTimeout f 1000
let task3 =
fun timelineResult previousResult ->
log "-----------task3 started..."
// delay-------------------------------
let f = fun _ ->
log "...task3 done"
timelineResult
|> nextTN (NullableT "task3")
|> ignore
setTimeout f 3000
let timelineStarter = Timeline Null //tasks disabled initially
let task123 =
task1 +| task2 +| task3
let taskOutput =
fun timelineResult (previousResult: NullableT<string>)
-> log ("The fastest result from: "
+ previousResult.Value)
timelineStarter
|> taskT task123 // Run all tasks then return the fastest result
|> taskT taskOutput // log the fastest result
|> ignore
let start =
fun _ -> // timeline will start
timelineStarter
|> nextTN nonNull
|> ignore
setTimeout start 2000
Demo
https://codepen.io/kentechgeek/pen/zYXQGwQ?editors=1111
| Contents |
|---|
| ? VanFS Versatility of Web Technology for Cross-Platform App Development |
| Getting Started |
| Web Components |
| ⚡️ Functional Reactive Programming (FRP) What is Functional Programming? How does Functional Programming Code Drive? |
| ⏱️ Timeline |
| ⏱️ Nullable Types What is Null, Nullable and Option Types? |
| ⏱️ Timeline Nullable |
| ⏱️ Timeline Task |
| ⏱️ Timeline Task Concat |
| ⏱️ Timeline Task Or |
| ⏱️ Timeline Task And |
| Discussions |
taskAnd or (+&)(Task -> Task) -> Tasklet task12 =
task1 +& task2
let task123 =
task1 +& task2 +& task3
let task1234 =
task123 +& task4module TaskAnd
open Van.TimelineElement // import Timeline
open Van.TimelineElementNullable // import Timelinetc.
open Van.Nullable // import NullableT
open Van.TimelineElementTask
open Van.TimelineElementTaskAnd
open System.Timers
let setTimeout f delay =
let timer = new Timer(float delay)
timer.AutoReset <- false
timer.Elapsed.Add(fun _ -> f())
timer.Start()
let nonNull = NullableT true
let task1 =
fun timelineResult previousResult ->
log "-----------task1 started..."
// delay-------------------------------
let f = fun _ ->
log "...task1 done"
timelineResult
|> nextTN (NullableT "task1")
|> ignore
setTimeout f 2500
let task2 =
fun timelineResult previousResult ->
log "-----------task2 started..."
// delay-------------------------------
let f = fun _ ->
log "...task2 done"
timelineResult
|> nextTN (NullableT "task2")
|> ignore
setTimeout f 1000
let task3 =
fun timelineResult previousResult ->
log "-----------task3 started..."
// delay-------------------------------
let f = fun _ ->
log "...task3 done"
timelineResult
|> nextTN (NullableT "task3")
|> ignore
setTimeout f 3000
let timelineStarter = Timeline Null //tasks disabled initially
let task123 =
task1 +& task2 +& task3
let taskOutput =
fun timelineResult (previousResult: NullableT<ListResult<'a>>)
-> log previousResult.Value.results
timelineStarter
|> taskT task123 // Run all tasks then return the list of results
|> taskT taskOutput // log the list of results
|> ignore
let start =
fun _ -> // timeline will start
timelineStarter
|> nextTN nonNull
|> ignore
setTimeout start 2000
Demo
https://codepen.io/kentechgeek/pen/poBmJZq?editors=1111