Skip to content

Commit eb509e5

Browse files
committed
chore: small fixes
1 parent a21af05 commit eb509e5

7 files changed

Lines changed: 90 additions & 70 deletions

File tree

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
"pyfile",
2323
"pyname",
2424
"Pyxpecto",
25+
"Sergey",
2526
"stroustrup",
27+
"Tihon",
2628
"xunit"
2729
]
2830
}

chapters/AsyncProgramming.fs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,7 @@ F# async shines when composing multiple operations:
7676

7777
let fetchMultipleAsync () =
7878
async {
79-
let! results =
80-
[ fetchDataAsync ()
81-
fetchDataAsync ()
82-
fetchDataAsync () ]
83-
|> Async.Parallel
79+
let! results = [ fetchDataAsync (); fetchDataAsync (); fetchDataAsync () ] |> Async.Parallel
8480

8581
return results |> Array.toList
8682
}
@@ -182,8 +178,10 @@ let taskExample () =
182178
let taskWithLoop () =
183179
task {
184180
let mutable sum = 0
181+
185182
for i in 1..10 do
186183
sum <- sum + i
184+
187185
return sum
188186
}
189187

@@ -279,16 +277,13 @@ Here's a pattern for async HTTP operations (assuming you have bindings for `aioh
279277
// Simulated async HTTP - in real code you'd use aiohttp bindings
280278
let fetchUrlAsync (url: string) =
281279
async {
282-
do! Async.Sleep 100 // Simulates network delay
280+
do! Async.Sleep 100 // Simulates network delay
283281
return $"Response from {url}"
284282
}
285283

286284
let fetchMultipleUrls (urls: string list) =
287285
async {
288-
let! responses =
289-
urls
290-
|> List.map fetchUrlAsync
291-
|> Async.Parallel
286+
let! responses = urls |> List.map fetchUrlAsync |> Async.Parallel
292287

293288
return responses |> Array.toList
294289
}
@@ -302,18 +297,17 @@ Choose based on whether operations are independent:
302297
let sequentialProcessing items =
303298
async {
304299
let results = ResizeArray()
300+
305301
for item in items do
306302
let! result = fetchUrlAsync item
307303
results.Add(result)
304+
308305
return results |> Seq.toList
309306
}
310307

311308
let parallelProcessing items =
312309
async {
313-
let! results =
314-
items
315-
|> List.map fetchUrlAsync
316-
|> Async.Parallel
310+
let! results = items |> List.map fetchUrlAsync |> Async.Parallel
317311
return results |> Array.toList
318312
}
319313

@@ -331,17 +325,18 @@ let cancellableWork (token: CancellationToken) =
331325
token.ThrowIfCancellationRequested()
332326
do! Async.Sleep 50
333327
printfn $"Step {i}"
328+
334329
return "Completed"
335330
}
336331

337332
let runWithTimeout () =
338333
async {
339-
use cts = new CancellationTokenSource(2000) // 2 second timeout
334+
use cts = new CancellationTokenSource(2000) // 2 second timeout
335+
340336
try
341337
let! result = cancellableWork cts.Token
342338
return Some result
343-
with
344-
| :? OperationCanceledException ->
339+
with :? OperationCanceledException ->
345340
return None
346341
}
347342

@@ -373,7 +368,11 @@ When working with Python frameworks that expect native async functions:
373368
let getItemTask (itemId: int) =
374369
task {
375370
do! Task.Delay 10
376-
return {| id = itemId; name = "Widget" |}
371+
372+
return {|
373+
id = itemId
374+
name = "Widget"
375+
|}
377376
}
378377

379378
(**
@@ -399,11 +398,7 @@ When you need rich composition primitives:
399398
let complexWorkflow () =
400399
async {
401400
// Run three operations in parallel
402-
let! results =
403-
[ fetchDataAsync ()
404-
fetchDataAsync ()
405-
fetchDataAsync () ]
406-
|> Async.Parallel
401+
let! results = [ fetchDataAsync (); fetchDataAsync (); fetchDataAsync () ] |> Async.Parallel
407402

408403
// Then do something sequential
409404
do! Async.Sleep 100

chapters/GettingStarted.fs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ module GettingStarted
33
(**
44
# Getting Started with Fable.Python
55
6-
Let's set up a Fable.Python project from scratch and get our first F# code
7-
running as Python.
6+
Let's set up a Fable.Python project from scratch and get our first F# code running as Python.
87
98
## Prerequisites
109
@@ -27,7 +26,7 @@ dotnet new console -lang F#
2726
2827
# Set up local tools and install Fable 5 (alpha)
2928
dotnet new tool-manifest
30-
dotnet tool install fable --version 5.0.0-alpha.20
29+
dotnet tool install fable --version 5.0.0-alpha.21
3130
3231
# Add Fable.Core package
3332
dotnet add package Fable.Core --version 5.0.0-beta.4
@@ -44,7 +43,7 @@ pip install "fable-library==5.0.0a20"
4443
---
4544
4645
**Note:** Version pinning matters. The fable-library version must match
47-
your Fable compiler version. PyPI uses `5.0.0a20` format instead of `5.0.0-alpha.20`.
46+
your Fable compiler version. PyPI uses `5.0.0a21` format instead of `5.0.0-alpha.21`.
4847
4948
---
5049
@@ -76,10 +75,10 @@ Transpile to Python:
7675
dotnet fable --lang python
7776
```
7877
79-
This creates `Program.py` in your project directory. Run it:
78+
This creates `program.py` in your project directory. Run it:
8079
8180
```bash
82-
python3 Program.py
81+
python3 program.py
8382
```
8483
8584
You should see:
@@ -106,7 +105,7 @@ After setup, your project looks like this:
106105
```text
107106
my-fable-python/
108107
├── Program.fs # Your F# source code
109-
├── Program.py # Generated Python (don't edit!)
108+
├── program.py # Generated Python (don't edit!)
110109
├── my-fable-python.fsproj
111110
├── fable_modules/ # Fable runtime modules
112111
└── .config/

chapters/Interop.fs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,30 @@ let factorial (count: int) : int =
214214
"""
215215

216216
(**
217+
### Py.python for Literal Python Code
218+
219+
`Py.python` provides a cleaner way to embed literal Python code without
220+
parameter placeholders. The code is printed as statements, so use Python's
221+
`return` keyword if you need to return a value:
222+
*)
223+
224+
open Fable.Python
225+
226+
let greet (name: string) : string =
227+
Py.python
228+
$"""
229+
greeting = f"Hello, {{name}}!"
230+
return greeting
231+
"""
232+
233+
(** This generates: *)
234+
(*** include-python: greet ***)
235+
236+
(**
237+
This is useful when you want to write a block of Python code directly,
238+
especially when it doesn't need parameter substitution (you can use F#
239+
string interpolation instead).
240+
217241
## StringEnum: Type-Safe String Constants
218242
219243
`StringEnum` creates discriminated unions that compile to Python strings:

chapters/Introduction.fs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ module Introduction
33
(**
44
# Introduction to Fable.Python
55
6-
> This post is part of the [F# Advent Calendar 2025](https://sergeytihon.com/2025/11/03/f-advent-calendar-in-english-2025/).
7-
Thank you, Sergey Tihon, for organizing this wonderful tradition that brings the F# community together every year!
6+
> This post is part of the [F# Advent Calendar
7+
2025](https://sergeytihon.com/2025/11/03/f-advent-calendar-in-english-2025/). Thank you, Sergey Tihon, for organizing
8+
this wonderful tradition that brings the F# community together every year!
89
910
Welcome to this guide on [Fable.Python](https://github.com/fable-compiler/Fable.Python/) -
1011
a compiler that transforms F# code into Python.
1112
1213
## What is Fable?
1314
14-
[Fable](https://fable.io/) is a compiler that brings F# to different platforms. While
15-
Fable is best known for compiling F# to JavaScript, it also supports other targets
15+
[Fable](https://fable.io/) is a compiler that brings F# to different platforms and ecosystems. While
16+
Fable is best known for compiling F# to TypeScript and JavaScript, it also supports other targets
1617
including Python, Rust, and Dart.
1718
1819
## Why Fable.Python?
@@ -26,9 +27,9 @@ F# is a functional-first language with powerful features like:
2627
2728
With Fable.Python, you get all these benefits while targeting the Python ecosystem.
2829
29-
Python is the [most popular programming language](https://www.tiobe.com/tiobe-index/)
30-
in the world. And no matter what you think of Python, it will always be the second
31-
best language for everything. That ubiquity is exactly why Fable.Python exists.
30+
Python is currently [the most popular programming language in the world](https://www.tiobe.com/tiobe-index/). And no
31+
matter what you think of Python, it will always be the second best language for everything. That ubiquity is exactly why
32+
Fable.Python exists.
3233
3334
## When to Use Fable.Python
3435
@@ -68,12 +69,10 @@ When compiled with Fable, this generates the following Python:
6869
(*** include-python: greet, message ***)
6970

7071
(**
71-
The `name: Any | None = None` signature may look odd at first. This happens because
72-
F# infers the type from usage - since we only call `greet` with a string, the compiler
73-
doesn't know if it might also be called with unit `()` (no argument). If it were,
74-
Python would call it as `greet()` instead of `greet("Fable.Python")`. Adding an
75-
explicit type annotation `let greet (name: string) = ...` would generate a cleaner
76-
`name: str` parameter.
72+
The `name: Any | None = None` signature may look odd at first. This happens because F# infers the type from usage -
73+
since we only call `greet` with a string, the compiler doesn't know if it might also be called with unit `()` (no
74+
argument). If it were, Python would call it as `greet()` instead of `greet("Fable.Python")`. Adding an explicit type
75+
annotation `let greet (name: string) = ...` would generate a cleaner `name: str` parameter.
7776
7877
## The Power of Types
7978
@@ -94,16 +93,14 @@ let shapes = [ Circle 5.0; Rectangle(3.0, 4.0) ]
9493
let totalArea = shapes |> List.sumBy area
9594

9695
(**
97-
This compiles to Python while preserving the semantic meaning. The `Shape` type
98-
becomes a tagged class structure, and the `match` expression becomes clean
99-
conditional logic. The compiler ensures you handle all cases - if you add a
100-
new shape variant, the compiler will warn you about unhandled cases in
101-
the `area` function.
96+
This compiles to Python while preserving the semantic meaning. The `Shape` type becomes a tagged class structure, and
97+
the `match` expression becomes clean conditional logic. The compiler ensures you handle all cases, i.e if you add a new
98+
shape variant, the compiler will warn you about unhandled cases in the `area` function.
10299
103100
## What's Next?
104101
105-
In the following chapters, we'll explore setting up your environment,
106-
working with Python libraries, and understanding F# compatibility with Fable.
102+
In the following chapters, we'll explore setting up your environment, working with Python libraries, and understanding
103+
F# compatibility with Fable.
107104
108105
Let's begin.
109106
*)

chapters/Pydantic.fs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,13 @@ type Product() =
9898
member val Name: string = "" with get, set
9999

100100
// Field with description
101-
member val Description: Field<string> =
102-
Field.Description "Product description" with get, set
101+
member val Description: Field<string> = Field.Description "Product description" with get, set
103102

104103
// Field with numeric constraints
105-
member val Price: Field<float> =
106-
Field.Ge 0.0 with get, set // price >= 0
104+
member val Price: Field<float> = Field.Ge 0.0 with get, set // price >= 0
107105

108106
// Field with string constraints
109-
member val Sku: Field<string> =
110-
Field.Pattern "^[A-Z]{2}-[0-9]{4}$" with get, set // e.g., "AB-1234"
107+
member val Sku: Field<string> = Field.Pattern "^[A-Z]{2}-[0-9]{4}$" with get, set // e.g., "AB-1234"
111108

112109
(**
113110
Available field constraints:
@@ -165,6 +162,7 @@ let customer = Customer.create 1 "Alice" (Some "alice@example.com")
165162

166163
let showCustomer (c: Customer) =
167164
printfn "Customer %d: %s" c.id c.name
165+
168166
match c.email with
169167
| Some email -> printfn " Email: %s" email
170168
| None -> printfn " No email on file"
@@ -210,10 +208,10 @@ let serializationExample () =
210208
user.Email <- Some "alice@example.com"
211209

212210
// Convert to dictionary
213-
let dict = user.model_dump()
211+
let dict = user.model_dump ()
214212

215213
// Convert to JSON string
216-
let json = user.model_dump_json()
214+
let json = user.model_dump_json ()
217215

218216
// Pretty-printed JSON
219217
let prettyJson = user.model_dump_json_indented 2
@@ -254,12 +252,15 @@ This distinction is important for well-architected applications:
254252
/// Domain model - uses precise F# types
255253
type UserId = UserId of System.Guid
256254

257-
type Money = { Amount: decimal; Currency: string }
255+
type Money = {
256+
Amount: decimal
257+
Currency: string
258+
}
258259

259260
type DomainUser = {
260261
Id: UserId
261262
Name: string
262-
Age: int32 // Bounded, wrapping arithmetic
263+
Age: int32 // Bounded, wrapping arithmetic
263264
Balance: Money
264265
}
265266

@@ -282,7 +283,11 @@ Explicit transformation between domain and DTO:
282283
module UserMapping =
283284
let toDTO (user: DomainUser) : UserDTO =
284285
let dto = UserDTO()
285-
dto.Id <- match user.Id with UserId guid -> string guid
286+
287+
dto.Id <-
288+
match user.Id with
289+
| UserId guid -> string guid
290+
286291
dto.Name <- user.Name
287292
dto.Age <- int user.Age
288293
dto.BalanceAmount <- float user.Balance.Amount
@@ -292,7 +297,7 @@ module UserMapping =
292297
let fromDTO (dto: UserDTO) : Result<DomainUser, string> =
293298
try
294299
Ok {
295-
Id = UserId (System.Guid.Parse dto.Id)
300+
Id = UserId(System.Guid.Parse dto.Id)
296301
Name = dto.Name
297302
Age = int32 dto.Age
298303
Balance = {

chapters/Python.fs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,16 @@ module ForPythonDevelopers
33
(**
44
# Are You a Python Developer?
55
6-
If you're coming from Python, welcome. This chapter will help you understand
7-
the F# code you'll see throughout this guide. F# is more approachable than
8-
it might appear, and many concepts are familiar.
6+
If you're coming from Python, welcome. This chapter will help you understand the F# code you'll see throughout this
7+
guide. F# is more approachable than it might appear, and many concepts are familiar.
98
109
## What is F#?
1110
12-
F# is a functional-first language that runs on .NET. But here's the key insight
13-
for you: **with Fable.Python, .NET is just a build tool**. You write F#, it
14-
compiles to Python, and you run Python. Your deployment is pure Python.
11+
F# is a functional-first language that runs on .NET. But here's the key insight for you: **with Fable.Python, .NET is
12+
just a build tool**. You write F#, it compiles to Python, and you run Python. Your deployment is pure Python.
1513
16-
Think of it like TypeScript for JavaScript - you get better tooling and type
17-
safety during development, but the output is the language you know.
14+
Think of it like TypeScript for JavaScript - you get better tooling and type safety during development, but the output
15+
is the language you know.
1816
1917
## Key Concepts You'll See
2018

0 commit comments

Comments
 (0)