Skip to content

Update docs #76

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

Merged
merged 5 commits into from
Sep 25, 2022
Merged
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
3 changes: 2 additions & 1 deletion Build.fs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Target.create "Build" (fun _ ->
)

Target.create "Docs" (fun _ ->
run poetry "run jb build docs-src --path-output docs" "."
run poetry $"run jb build docs-src --path-output {buildPath}/docs" "."
Fake.IO.Shell.copyDir "docs" $"{buildPath}/docs/_build/html" (fun _ -> true)
)

Target.create "Run" (fun _ ->
Expand Down
61 changes: 20 additions & 41 deletions docs-src/dotnet/numbers.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,43 @@
# Numeric types

In Fable, we use F# numeric types, which are all translated to Python integers at the exception of `float`, `double`, and `decimal`.
In Fable, we use F# numeric types, which are all translated to Python integers except for `float`, `double`, and
`decimal`.

Fable numbers are very nearly compatible with .NET semantics, but translating into Python types has consequences:

* (non-standard) All floating point numbers are implemented as 64 bit (`double`). This makes `float32` numbers more accurate than expected.
* (non-standard) Arithmetic integers of 32 bits or less are implemented with different truncation from that expected, as whole numbers embedded within `double`.
* (non-standard) All floating point numbers are implemented as 64 bit (`double`). This makes `float32` numbers more
accurate than expected.
* (non-standard) Arithmetic integers of 32 bits or less are implemented with different truncation from that expected, as
whole numbers embedded within `double`.
* (OK) Conversions between types are correctly truncated.
* (OK) Bitwise operations for 64 bit and 32 bit integers are correct and truncated to the appropriate number of bits.
* (non-standard) Bitwise operations for 16 bit and 8 bit integers use the underlying JavaScript 32 bit bitwise semantics. Results are not truncated as expected, and shift operands are not masked to fit the data type.
* (OK) Longs have a custom implementation which is identical in semantics to .NET and truncates in 64 bits, although it is slower.
* (non-standard) Bitwise operations for 16 bit and 8 bit integers use the underlying JavaScript 32 bit bitwise
semantics. Results are not truncated as expected, and shift operands are not masked to fit the data type.
* (OK) Longs have a custom implementation which is identical in semantics to .NET and truncates in 64 bits, although it
is slower.

32 bit integers thus differ from .NET in two ways:

* Underlying 52 bit precision, without expected truncation to 32 bits on overflow. Truncation can be forced if needed by `>>> 0`.
* On exceeding 52 bits absolute value floating point loses precision. So overflow will result in unexpected lower order 0 bits.
* Underlying unlimited precision, without expected truncation to 32 bits on overflow. Truncation can be forced if needed
by `>>> 0`.

The loss of precision can be seen in a single multiplication:

```fsharp
((1 <<< 28) + 1) * ((1 <<< 28) + 1) >>> 0
```

The multiply product will have internal double representation rounded to `0x0100_0000_2000_0000`. When it is truncated to 32 bits by `>>> 0` the result will be `0x2000_0000` not the .NET exact lower order bits value of `0x2000_0001`.

The same problem can be seen where repeated arithmetic operations make the internal (non-truncated) value large. For example a linear congruence random number generator:

```fsharp
let rng (s:int32) = 10001*s + 12345
```

The numbers generated by repeated application of this to its result will all be even after the 4th pseudo-random number, when `s` value exceeds 2^53:

```fsharp
let rec randLst n s =
match n with
| 0 -> [s]
| n -> s :: randLst (n-1) (rng s)

List.iter (printfn "%x") (randLst 7 1)
```

The resulting printed list of pseudo-random numbers does not work in Fable:

| Fable | .NET |
|-------:|------:|
|1|1|
|574a|574a
|d524223|d524223|
|6a89e98c|6a89e98c|
|15bd0684|15bd0685|
|3d8b8000|3d8be20e|
|50000000|65ba5527|
|0|2458c8d0|
The multiply product will have internal double representation rounded to `0x0100_0000_2000_0000`. When it is truncated
to 32 bits by `>>> 0` the result will be `0x2000_0000` not the .NET exact lower order bits value of `0x2000_0001`.

## Workarounds

* When accurate low-order bit arithmetic is needed and overflow can result in numbers larger than 2^53 use `int64`, `uint64`, which use exact 64 bits, instead of `int32`, `uint32`.
* Alternately, truncate all arithmetic with `>>> 0` or `>>> 0u` as appropriate before numbers can get larger than 2^53: `let rng (s:int32) = 10001*s + 12345 >>> 0`
* When accurate low-order bit arithmetic is needed and overflow can result in numbers larger than 2^53 use `int64`,
`uint64`, which use exact 64 bits, instead of `int32`, `uint32`.
* Alternately, truncate all arithmetic with `>>> 0` or `>>> 0u` as appropriate before numbers can get larger than 2^53:
`let rng (s:int32) = 10001*s + 12345 >>> 0`

## Printing

One small change from .NET in `printf`, `sprintf`, `ToString`. Negative signed integers are printed in hexadecimal format as sign + magnitude, in .NET they are printed as two's complement bit patterns.
One small change from .NET in `printf`, `sprintf`, `ToString`. Negative signed integers are printed in hexadecimal
format as sign + magnitude, in .NET they are printed as two's complement bit patterns.
38 changes: 20 additions & 18 deletions docs-src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Fable supports the <a href="docs/dotnet/compatibility.html">F# core library and
::::

```{seealso}
[Try online](https://fable.io/repl")
[Try online](https://fable.io/repl)
```

## Features
Expand Down Expand Up @@ -130,21 +130,23 @@ let thisWorks = distance / time
Build your types using real-world conditions and make the compiler warn you if those conditions change.

```fsharp
[<Literal>]
let JSON_URL = "https://jsonplaceholder.typicode.com/todos"

// Type is created automatically from the url
type Todos = Fable.JsonProvider.Generator<JSON_URL>

async {
let! (_, res) = Fable.SimpleHttp.Http.get url
let todos = Todos.ParseArray res
for todo in todos do
// Compilation fail if the JSON schema changes
printfn "ID %i, USER: %i, TITLE %s, COMPLETED %b"
todo.id
todo.userId
todo.title
todo.completed
}
open Zanaptak.TypedCssClasses

type Bulma = CssClasses<"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css", Naming.PascalCase>

let hero (model: Model) body =
Html.section [
prop.classes [ Bulma.Hero; Bulma.IsFullheightWithNavbar ]
prop.style [
style.backgroundImageUrl (model.Banner)
style.backgroundPosition "center"
style.backgroundSize.cover
]
prop.children [
Html.div [
prop.classes [ Bulma.HeroBody; Bulma.IsDark ]
prop.children [ Html.div [ prop.classes [ Bulma.Container ]; prop.children body ] ]
]
]
]
```
11 changes: 8 additions & 3 deletions docs-src/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ printfn $"The area of the square is {getArea square}"

## What is F#?

F# (pronounced f-sharp) is a strongly typed Functional programming language which offers many great features to build robust and maintable code such as:
F# (pronounced f-sharp) is a strongly typed Functional programming language which offers many great features to build
robust and maintable code such as:

- Lightweight syntax
- Immutability baked into the language by default
- Rich types which let you represent easily your data or your domain
- Powerful pattern matching to define complex behaviors
- And so much more...

F# is already used on the server for web and cloud apps, and it's also used quite a lot for data science and machine learning. It's a great general-purpose language, and also a great fit for building beautiful UIs that run in the browser.
F# is already used on the server for web and cloud apps, and it's also used quite a lot for data science and machine
learning. It's a great general-purpose language, and also a great fit for building beautiful UIs that run in the
browser.

## Why use F# for your next Python project?

Expand All @@ -39,4 +42,6 @@ F# is a great choice to build beautiful apps that run in the browser. F# is:

When compared with Python, F# is safer, more robust, and more pleasant to read and write.

F# is a mature language with functional programming and object programming capabilities, but it doesn't sacrifice readability or simplicity to offer these things. Because of that, we think it's a great choice for your next Python application.
F# is a mature language with functional programming and object programming capabilities, but it doesn't sacrifice
readability or simplicity to offer these things. Because of that, we think it's a great choice for your next Python
application.
16 changes: 12 additions & 4 deletions docs-src/introduction/dotnet-users-read-this.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,24 @@ For starters, using F# for fable apps is very similar to using F# for .NET apps:
1. Fable projects use standard `.fsproj` project files (which can also be part of `.sln` solutions) or `.fsx` scripts
2. You can use well-known .NET/F# tools like NuGet, Paket or Fake to manage dependencies or your build
3. You can use any F# editor like Visual Studio, Ionide or Rider
4. Many common classes and routines from the **System** namespace have been mapped to Fable to provide a familiar interface for very common things, such as `DateTime`, `Math`, `String`, etc.
4. Many common classes and routines from the **System** namespace have been mapped to Fable to provide a familiar
interface for very common things, such as `DateTime`, `Math`, `String`, etc.
5. Most of **FSharp.Core** is also supported. Check the ".NET and F# Compatibility" section.

## Fable is Python

Although Fable brings a lot of familiarity for F# and .NET developers, the target runtime is Python. This difference impacts several important areas:
Although Fable brings a lot of familiarity for F# and .NET developers, the target runtime is Python. This difference
impacts several important areas:

* Your dependencies will be Python (e.g PyPI) dependencies, not .NET dependencies
* Runtime behavior will be based on Python semantics, not .NET semantics

There are some NuGet packages you can add into an F# project, but these packages have to also target Fable Python. Otherwise, your dependencies will directly be Python dependencies. Please check the "Author a Fable library" section to learn more, especially if you are a library author and would like to make your package compatible with Fable Python.
There are some NuGet packages you can add into an F# project, but these packages have to also target Fable Python.
Otherwise, your dependencies will directly be Python dependencies. Please check the "Author a Fable library" section to
learn more, especially if you are a library author and would like to make your package compatible with Fable Python.

These differences may seem like a lot to take in at once, but they're important for building robust applications. There have been efforts in the past to try and "magically" turn .NET code into JS, and these all tend to fail. The reality is that your target runtime environment will affect how your app runs and how you build it. However, once you internalize this, you'll find that using F# for web apps with Fable is incredibly productive, making web programming a lot more joyful than just using Python!
These differences may seem like a lot to take in at once, but they're important for building robust applications. There
have been efforts in the past to try and "magically" turn .NET code into JS, and these all tend to fail. The reality is
that your target runtime environment will affect how your app runs and how you build it. However, once you internalize
this, you'll find that using F# for web apps with Fable is incredibly productive, making web programming a lot more
joyful than just using Python!
15 changes: 15 additions & 0 deletions docs-src/your-fable-project/build-and-run.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@

Your project is ready? Then it's time to build and run it.

## Setup environment

```console
> dotnet new tool-manifest
> dotnet tool install fable --prerelease
```

## Build

If you are buliding an F# script:

```console
> dotnet fable --lang py project.fsx
```

If you are buliding an F# project:

```console
> dotnet fable --lang py project.fsproj
```

## Run

```console
Expand Down
37 changes: 18 additions & 19 deletions docs-src/your-fable-project/project-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ Many F# IDEs already provide commands to perform operations like creating a proj
below are only necessary if you want to do this manually.
:::

<ul class="textual-steps">
<li>
## Create an .fsproj file

**Create an .fsproj file**

`.fsproj` files are in XML format. This may look a bit old-fashioned, but luckily the basic schema for F# projects has become much simpler in recent versions. Now the skeleton for most projects looks like this:
`.fsproj` files are in XML format. This may look a bit old-fashioned, but luckily the basic schema for F# projects has
become much simpler in recent versions. Now the skeleton for most projects looks like this:

```xml
<Project Sdk="Microsoft.NET.Sdk">
Expand All @@ -31,21 +29,19 @@ below are only necessary if you want to do this manually.
</Project>
```

</li>

<li>

**Add an .fs file to the project**
## Add an .fs file to the project

F# source files end with the `.fs` extension. To include a new one in the project, you only need to add it to the `.fsproj` using a relative path with the following tag:
F# source files end with the `.fs` extension. To include a new one in the project, you only need to add it to the
`.fsproj` using a relative path with the following tag:

```xml
<ItemGroup>
<Compile Include="path/to/my/File.fs" />
<ItemGroup>
```

For example, if we have have an app with two files, named `MyAwesomeFeature.fs` and `App.fs` (the last one contains the entry point), it will look like this
For example, if we have have an app with two files, named `MyAwesomeFeature.fs` and `App.fs` (the last one contains the
entry point), it will look like this

```xml
<ItemGroup>
Expand All @@ -55,12 +51,15 @@ For example, if we have have an app with two files, named `MyAwesomeFeature.fs`
```

:::{note}
Please be aware that in F#, **file order is important.** For instance, if `App.fs` calls `MyAwesomeFeature.fs`, then you must place `MyAwesomeFeature.fs` above `App.fs`.
Please be aware that in F#, **file order is important.** For instance, if `App.fs` calls `MyAwesomeFeature.fs`, then you
must place `MyAwesomeFeature.fs` above `App.fs`.
:::

Let's add another file, `Authentication.fs`, located in another folder `Shared` which is at the same depth as our `src` folder (this can happen, for example, if the file is shared with another project, like a server). Let's see the current state of our project tree:
Let's add another file, `Authentication.fs`, located in another folder `Shared` which is at the same depth as our `src`
folder (this can happen, for example, if the file is shared with another project, like a server). Let's see the current
state of our project tree:

```
```text
myproject
|_ src
|_ MyAwesomeFeature.fs
Expand All @@ -80,7 +79,7 @@ This can be expressed in the project file as:
<ItemGroup>
```

</li>
</ul>

An important thing to note is Fable will translate F# source files to [ES2015 modules](https://exploringjs.com/es6/ch_modules.html) using JS `import`. This means that files which are not referenced by any other **will be ignored** (except the last file) including side effects. This is different behavior from .NET, where all files are compiled and executed, regardless of their relationship with the rest of the project.
An important thing to note is Fable will translate F# source files to [ES2015
modules](https://exploringjs.com/es6/ch_modules.html) using JS `import`. This means that files which are not referenced
by any other **will be ignored** (except the last file) including side effects. This is different behavior from .NET,
where all files are compiled and executed, regardless of their relationship with the rest of the project.
2 changes: 1 addition & 1 deletion docs-src/your-fable-project/testing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Testing

You can use all tools of the JS ecosystem.
You can use all tools of the Python ecosystem.

Several js libs already have a Fable binding:
- mocha: [https://github.com/Zaid-Ajaj/Fable.Mocha](https://github.com/Zaid-Ajaj/Fable.Mocha)
Expand Down
Loading