Skip to content

Commit 2b632af

Browse files
author
Maxime Mangel
committed
New blog - Glutinum, a new era
1 parent 3f92b62 commit 2b632af

File tree

2 files changed

+241
-1
lines changed

2 files changed

+241
-1
lines changed

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44
"markdown",
55
"plaintext",
66
"text"
7-
]
7+
],
8+
"files.trimFinalNewlines": true,
9+
"files.trimTrailingWhitespace": true,
10+
"files.insertFinalNewline": true
811
}
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
---
2+
layout: fable-blog-page
3+
title: Glutinum, a new era for Fable bindings
4+
author: Mangel Maxime
5+
date: 2024-01-01
6+
author_link: https://twitter.com/MangelMaxime
7+
author_image: https://github.com/MangelMaxime.png
8+
# external_link:
9+
abstract: |
10+
Let me introduce Glutinum, a new ecosystem for Fable bindings aiming to provide the best F# experience while staying close to the original JavaScript API.
11+
---
12+
13+
2 years ago, I soft-launched **Glutinum** project with the goal to push Fable bindings to the next level.
14+
15+
Thanks to new innovations in Fable, and after working on 20+ bindings, I am now ready to say it is possible to provide near native F# experience while staying close to the original JavaScript API.
16+
17+
The former allows F# developers to consume bindings with minimal friction. While the later makes it easier to re-use knowledge and documentation coming from the original JavaScript community.
18+
19+
With this confirmation, I started prototyping a new tool to convert TypeScript definitions to F#.
20+
21+
## Why a new tool?
22+
23+
For a long time, I have been split between re-writing ts2fable or creating a new tool. In the end, I decided to create a new tool because my goal is not only to provide a new converter but also a completely new set of bindings for Fable.
24+
25+
Creating a new ecosystem makes it possible to progressively migrate to the new bindings without breaking existing libraries.
26+
27+
## How is it different from ts2fable?
28+
29+
I don't want to go into too much details about the design decision behind Glutinum CLI, but here are some highlights.
30+
31+
### Minimize erased union types
32+
33+
To me, the first source of friction when consuming Fable bindings is the usage of erased union types (`U2`, `U3`, etc.).
34+
35+
Glutinum CLI minimizes the usage of erased union types by 2 main ways:
36+
37+
#### 1. Inline values when possible
38+
39+
Example based on **enum inheritance**.
40+
41+
<div class="has-text-centered mb-3">
42+
43+
**TypeScript**
44+
45+
</div>
46+
47+
```ts
48+
export type ColorA =
49+
| 'black'
50+
51+
export type ColorB =
52+
| 'bgBlack'
53+
54+
export type Color = ColorA | ColorB;
55+
```
56+
57+
<div class="columns is-multiline is-mobile" data-disable-copy-button>
58+
<div class="column is-6 has-text-centered">
59+
60+
**ts2fable**
61+
62+
</div>
63+
<div class="column is-6 has-text-centered">
64+
65+
**Glutinum**
66+
67+
</div>
68+
<div class="column is-6">
69+
70+
```fs
71+
[<StringEnum>]
72+
[<RequireQualifiedAccess>]
73+
type ColorA =
74+
| Black
75+
76+
[<StringEnum>]
77+
[<RequireQualifiedAccess>]
78+
type ColorB =
79+
| BgBlack
80+
81+
type Color =
82+
U2<ColorA, ColorB>
83+
```
84+
85+
</div>
86+
<div class="column is-6">
87+
88+
```fs
89+
[<RequireQualifiedAccess>]
90+
[<StringEnum(CaseRules.None)>]
91+
type ColorA =
92+
| black
93+
94+
[<RequireQualifiedAccess>]
95+
[<StringEnum(CaseRules.None)>]
96+
type ColorB =
97+
| bgBlack
98+
99+
[<RequireQualifiedAccess>]
100+
[<StringEnum(CaseRules.None)>]
101+
type Color =
102+
| black
103+
| bgBlack
104+
```
105+
106+
</div>
107+
</div>
108+
109+
#### 2. Generate multiple overloads
110+
111+
In JavaScript it is common for a function/method to accept different types for the same argument. In such case, we can avoid the union type by generating multiple overloads.
112+
113+
**TypeScript**
114+
115+
```ts
116+
export class Dayjs {
117+
locale(preset: string | ILocale): Dayjs;
118+
}
119+
```
120+
121+
<div data-disable-copy-button>
122+
123+
**ts2fable**
124+
125+
```fs
126+
type [<AllowNullLiteral>] Dayjs =
127+
abstract locale: preset: U2<string, ILocale> -> Dayjs
128+
```
129+
130+
**Glutinum**
131+
132+
```fs
133+
[<AllowNullLiteral>]
134+
type Dayjs =
135+
abstract member locale: preset: ILocale -> Dayjs
136+
abstract member locale: preset: string -> Dayjs
137+
```
138+
139+
</div>
140+
141+
### Increased understanding of TypeScript utilities
142+
143+
TypeScript **loves** to offer utility types to their users, so they can avoid code duplication / have a more interwoven type system.
144+
145+
Glutinum CLI tries to understand those utilities and generates the best possible F# representation.
146+
147+
For example, in the following example, `ts2fable` will generate an erased type `KeyOf` to mimic the behavior of `keyof` in TypeScript. Glutinum will instead generate a string enums with the literal values coming from the interface properties names.
148+
149+
<div class="has-text-centered mb-3">
150+
151+
**TypeScript**
152+
153+
</div>
154+
155+
```ts
156+
export interface Point {
157+
x: number;
158+
y: number;
159+
}
160+
161+
type P = keyof Point;
162+
```
163+
164+
<div class="columns is-multiline is-mobile" data-disable-copy-button>
165+
<div class="column is-6 has-text-centered">
166+
167+
**ts2fable**
168+
169+
</div>
170+
<div class="column is-6 has-text-centered">
171+
172+
**Glutinum**
173+
174+
</div>
175+
<div class="column is-6">
176+
177+
```fs
178+
[<Erase>]
179+
type KeyOf<'T> = Key of string
180+
181+
type [<AllowNullLiteral>] Point =
182+
abstract x: float with get, set
183+
abstract y: float with get, set
184+
185+
type P =
186+
KeyOf<Point>
187+
```
188+
189+
</div>
190+
<div class="column is-6">
191+
192+
```fs
193+
[<AllowNullLiteral>]
194+
type Point =
195+
abstract member x: float with get, set
196+
abstract member y: float with get, set
197+
198+
[<RequireQualifiedAccess>]
199+
[<StringEnum(CaseRules.None)>]
200+
type P =
201+
| x
202+
| y
203+
```
204+
205+
</div>
206+
</div>
207+
208+
Here is as list of others ideas I have in mind:
209+
210+
- Benefit from F# ability to open `static type`, so we can have access to more situations where we can avoid erased union types.
211+
- Have a plugin interface to generate specific code
212+
- Generates Feliz DSL when a library returns a `ReactElement`
213+
- Generate named erased union instead of `U2`, `U3`, etc. allowing for a more declarative code.
214+
- and much more...
215+
216+
## Can I try it?
217+
218+
For sure! 🎉
219+
220+
The easiest way to get started is to use `npx` to run the CLI without installing it:
221+
222+
```sh
223+
npx @glutinum/cli path/file.d.ts
224+
```
225+
226+
You can also install it locally:
227+
228+
```sh
229+
npm install @glutinum/cli
230+
```
231+
:::warning{title="Warning"}
232+
Glutinum CLI is still in early development, not all of TypeScript syntax or planned optimizations are supported yet.
233+
234+
Currently, if the CLI encounters an unsupported syntax it will most likely crash. I am still working on making it more tolerant to unsupported syntax.
235+
:::
236+
237+
I wish you all a happy new year and I hope you will follow me in this new adventure. 🎉

0 commit comments

Comments
 (0)