Skip to content

Commit ca23e7c

Browse files
authored
Merge pull request #961 from IanWitham/gh-pages
Code snippet carousel
2 parents a7e8c10 + a6c0fa6 commit ca23e7c

15 files changed

+567
-18
lines changed

README.md

+49-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,53 @@
22

33
This repository hosts [The F# Software Foundation web site](https://fsharp.org/). Pull requests are welcome. The web site code is hosted in the `gh-pages` branch and is created using the tools provided by GitHub:
44

5+
* Fork this repository
56
* Switch to [the `gh-pages` branch](https://github.com/fsharp/fsharp.org/tree/gh-pages)
6-
* Read [documentation for Github Pages](https://help.github.com/categories/20/articles)
77
* The site is using [Jekyll for templating](http://jekyllrb.com/docs/usage/)
88

9-
## Development
9+
## Editing content
1010

11-
The easiest way to get started with this repository is by using the supplied dev container.
11+
Most edits can be made by simply editing the appropriate markdown file. If you prefer to make your edit directly within github, there is a github actions which should build your fork automatically so you can confirm your changes look okay.
12+
13+
## Testimonials
14+
15+
New testimonials are very welcome!
16+
17+
Simply add a new markdown file to the `_testimonials` folder. Take a look at the existing testimonials for the correct formatting and YAML frontmatter.
18+
19+
## Code snippet/examples
20+
21+
The code examples in the "code snippet carousel" are kept as individual markdown files in the `_snippets` folder. Please check the structure of existing snippets if you'd like to add a new one.
22+
23+
The "code content" is in the YAML frontmatter and requires the "literal scalar block style", i.e. start with a `|` character, and indent the code block by four spaces.
24+
25+
Note also the use of an excerpt separator `<!--more-->`. This is to ensure that only the content above that separator will appear on mobile (due to space constraints).
26+
27+
Example:
28+
29+
---
30+
order: 0
31+
title: HelloWorld.fs
32+
excerpt_separator: <!--more-->
33+
code: |
34+
let hello name =
35+
printfn $"Hello, {name}!"
36+
37+
hello "github"
38+
---
39+
## I am the title
40+
41+
I am the introdoctory paragraph. Mobile and desktop users can see me.
42+
<!--more-->
43+
- **Desktop users** can see this extra content
44+
- **Mobile users** will miss out
45+
46+
Not ideal, but oh well.
47+
48+
49+
## Developing locally
50+
51+
The easiest way to get started developing this repository on your own machine is by using the supplied dev container.
1252

1353
### Using the Dev Container
1454

@@ -21,6 +61,12 @@ A dev container is a pre-configured development environment that includes all th
2161

2262
If not using VSCode, consult your preferred IDE's documentation for instructions.
2363

64+
You may need to run the following once to install TailwindCSS dependency:
65+
66+
```
67+
npm i
68+
```
69+
2470
To start the development server, run the following command:
2571

2672
```sh

_config.yml

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ collections:
1818
testimonials:
1919
output: false
2020
permalink: /testimonials/#:id
21+
snippets:
22+
output: false
23+
sort_by: order
24+
excerpt_enabled: false
2125

2226

2327

_includes/navbar.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
<a href="{{ '/teaching/research' | relative_url }}" role="menuitem" class="group"
7171
tabindex="-1">Publications</a>
7272
<a href="https://www.youtube.com/c/fsharporg" role="menuitem" class="group"
73-
tabindex="-1">Videos (FSFF channel)</a>
73+
tabindex="-1">Videos (FSSF channel)</a>
7474
<a href="https://www.youtube.com/results?search_query=learn+f%23" role="menuitem"
7575
class="group" tabindex="-1">Videos (Community)</a>
7676
</div>

_layouts/main.html

+12-1
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,26 @@
2525
<meta name="msapplication-TileColor" content="#2d3947">
2626
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
2727

28+
<!-- fonts -->
2829
<link rel="preconnect" href="https://fonts.googleapis.com">
2930
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
30-
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Roboto+Flex:opsz,[email protected],100..1000&display=swap" rel="stylesheet">
31+
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Roboto+Flex:opsz,[email protected],100..1000&display=swap" rel="stylesheet">
3132

33+
<!-- icons -->
3234
<link href="{{ '/css/fontawesome6/css/fontawesome.css' | relative_url }}" rel="stylesheet" />
3335
<link rel="stylesheet" href="{{ '/css/fontawesome6/css/brands.min.css' | relative_url }}"" />
3436
<link rel="stylesheet" href="{{ '/css/fontawesome6/css/solid.min.css' | relative_url }}" />
3537

38+
<!-- Swiper -->
39+
<script src="
40+
https://cdn.jsdelivr.net/npm/[email protected]/swiper-bundle.min.js
41+
"></script>
42+
<link href="
43+
https://cdn.jsdelivr.net/npm/[email protected]/swiper-bundle.min.css
44+
" rel="stylesheet">
45+
3646
<link rel="stylesheet" href="{{ '/css/site.css' | relative_url }}" media="screen" />
47+
<link rel="stylesheet" href="{{ '/css/monokai.css' | relative_url }}" media="screen" />
3748

3849
<script>
3950
document.documentElement.classList.toggle(

_snippets/aoc_day1.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
order: 16
3+
title: AOC_Day1.fs
4+
excerpt_separator: <!--more-->
5+
code: |
6+
type Direction = Up | Down
7+
8+
let parseDirections chars =
9+
let parseDirection = function
10+
| '(' -> Up
11+
| ')' -> Down
12+
| _ -> failwith "Invalid character!"
13+
chars |> Seq.map parseDirection
14+
15+
let nextFloor currentFloor direction =
16+
match direction with
17+
| Up -> currentFloor + 1
18+
| Down -> currentFloor - 1
19+
20+
let findEndingFloor startingFloor directions =
21+
directions |> Seq.fold nextFloor startingFloor
22+
23+
"()(()((()((" |> parseChars |> findEndingFloor 0 |> printf
24+
---
25+
## Solve the Important Problems
26+
27+
F# excels at solving algorithmic challenges with clarity and precision. The code elegantly tracks an elevator's movement through a building by parsing directional instructions.
28+
<!--more-->
29+
- **Discriminated unions** to model directions clearly
30+
- **Pattern matching** for expressive, exhaustive handling of cases
31+
- **Higher-order functions** like `fold` to accumulate state
32+
- **Function composition** with the pipeline operator for readable data flow
33+
34+
Notice how the solution reads almost like a plain English description of the problem, making it both maintainable and self-documenting.

_snippets/computation_expressions.md

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
order: 17
3+
title: ComputationExpressions.fs
4+
excerpt_separator: <!--more-->
5+
code: |
6+
// Sequence generation
7+
let rec fizzBuzzSeq n =
8+
seq {
9+
yield
10+
match n with
11+
| x when x % 15 = 0 -> "fizzbuzz"
12+
| x when x % 3 = 0 -> "fizz"
13+
| x when x % 5 = 0 -> "buzz"
14+
| _ -> n.ToString()
15+
16+
yield! fizzBuzzSeq (n + 1)
17+
}
18+
19+
fizzBuzzSeq 1 |> Seq.take 100 |> Seq.iter (printfn "%s")
20+
21+
// Asynchronous programming with computation expressions
22+
let fetchDataAsync url = async {
23+
printfn "Fetching data from %s..." url
24+
do! Async.Sleep 1000 // Simulate network delay
25+
return sprintf "Data from %s" url
26+
}
27+
28+
// Custom computation expression for validation
29+
type ValidationBuilder() =
30+
member _.Bind(x, f) =
31+
match x with
32+
| Ok value -> f value
33+
| Error e -> Error e
34+
member _.Return(x) = Ok x
35+
member _.ReturnFrom(x) = x
36+
37+
let validate = ValidationBuilder()
38+
39+
type Person = { Name: string; Age: int }
40+
41+
// Using our custom computation expression
42+
let validatePerson age name = validate {
43+
let! validAge =
44+
if age >= 0 && age < 150 then Ok age
45+
else Error "Age must be between 0 and 150"
46+
47+
let! validName =
48+
if String.length name > 0 then Ok name
49+
else Error "Name cannot be empty"
50+
51+
return { Name = validName; Age = validAge }
52+
}
53+
54+
// Using multiple computation expressions together
55+
let processPersonAsync person = async {
56+
let result = validatePerson person.Age person.Name
57+
match result with
58+
| Ok validated ->
59+
return! fetchDataAsync $"profile/{validated.Name}"
60+
| Error msg ->
61+
return $"Validation error: {msg}"
62+
}
63+
64+
processPersonAsync { Name = "Snowdrop"; Age = 13}
65+
|> Async.RunSynchronously
66+
---
67+
## Expressive Control Flow with Computation Expressions
68+
69+
F# computation expressions provide an elegant syntax for complex control flows with a clean, readable notation that some say is F#'s superpower.
70+
<!--more-->
71+
- **Simplified asynchronous code** makes non-blocking operations read like synchronous code
72+
- **Custom control flow abstractions** create domain-specific mini-languages
73+
- **Seamless error handling** with [railway-oriented programming](https://fsharpforfunandprofit.com/rop/) patterns
74+
- **Elegant data transformations** by hiding boilerplate and focusing on business logic
75+
- **Composable workflows** that can be combined and nested for complex operations
76+

_snippets/domain_modelling.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
order: 15
3+
title: PaymentSystem.fs
4+
code: |
5+
type CardInfo = { Number: string; Expiry: string; Cvv: string }
6+
type BankInfo = { AccountNumber: string; RoutingNumber: string }
7+
type PayPalInfo = { Email: string; Token: string }
8+
9+
type PaymentMethod =
10+
| CreditCard of CardInfo
11+
| BankTransfer of BankInfo
12+
| PayPal of PayPalInfo
13+
14+
type Payment = {
15+
Amount: decimal
16+
Method: PaymentMethod
17+
}
18+
19+
let processPayment payment =
20+
match payment.Method with
21+
| CreditCard card ->
22+
printfn
23+
"Processing $%.2f via card %s"
24+
payment.Amount
25+
card.Number
26+
| BankTransfer bank ->
27+
printfn
28+
"Processing $%.2f via bank account %s"
29+
payment.Amount
30+
bank.AccountNumber
31+
| PayPal pp ->
32+
printfn
33+
"Processing $%.2f via PayPal account %s"
34+
payment.Amount
35+
pp.Email
36+
---
37+
## Making Invalid States Unrepresentable
38+
This example showcases F#'s ability to create precise domain models that prevent errors at compile time.
39+
40+
- **Discriminated unions** model each payment method with exactly the fields it needs
41+
- **No "impossible" states** can exist - a credit card payment can't have a routing number
42+
- **Exhaustive pattern matching** ensures every payment type is handled properly
43+
- **Type safety** catches errors at compile time that would be runtime bugs in other languages
44+
45+
By modeling your domain using F#'s algebraic data types, you create self-documenting code where the type system itself enforces business rules. This powerful technique shifts many bugs from runtime to compile time, dramatically improving software reliability.

_snippets/fable.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
order: 12
3+
title: WebApps.fs
4+
excerpt_separator: <!--more-->
5+
code: |
6+
open Browser.Dom
7+
open Feliz
8+
9+
// DOM manipulation
10+
let button = document.createElement("button")
11+
button.textContent <- "Click me!"
12+
button.addEventListener("click", fun _ ->
13+
window.alert("Hello from F#!")
14+
)
15+
document.body.appendChild(button) |> ignore
16+
17+
// React component (Feliz)
18+
let counter = React.functionComponent(fun () ->
19+
let (count, setCount) = React.useState(0)
20+
Html.div [
21+
Html.button [
22+
prop.text "-"
23+
prop.onClick (fun _ -> setCount(count - 1) )
24+
]
25+
Html.span [prop.text count]
26+
Html.button [
27+
prop.text "+"
28+
prop.onClick (fun _ -> setCount(count + 1) )
29+
]
30+
]
31+
)
32+
---
33+
## F# for JavaScript Development
34+
35+
F# isn't just for .NET development - with [F# web technologies]({{ '/use/web-apps/' | relative_url }}), you can target JavaScript environments directly.
36+
<!--more-->
37+
- **Type-safe DOM manipulation** catches errors at compile time, not runtime
38+
- **Seamless React integration** with hooks and modern patterns
39+
- **Full npm ecosystem access** with clean TypeScript-like interop
40+
- **Simplified async programming** with F#'s computation expressions for promises
41+
42+
F# brings its powerful type system and immutability to frontend development, eliminating common JavaScript bugs while maintaining full access to the JavaScript ecosystem.

_snippets/helloworld.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
order: 0
3+
title: HelloWorld.fs
4+
excerpt_separator: <!--more-->
5+
code: |
6+
let hello name =
7+
printfn $"Hello, {name}!"
8+
9+
let greets = [
10+
"World"
11+
"Solar System"
12+
"Milky Way Galaxy"
13+
"Local Galactic Group"
14+
"Virgo Supercluster"
15+
"Universe"
16+
"Omniverse"
17+
]
18+
19+
greets |> List.iter hello
20+
---
21+
## Concise and Expressive like Python
22+
23+
This simple "Hello World" example demonstrates F#'s elegant syntax and functional approach to programming.
24+
<!--more-->
25+
- **Concise function syntax** defines reusable functions with minimal boilerplate
26+
- **Clean list creation** uses indentation-based syntax without requiring commas
27+
- **String interpolation** provides readable string formatting with the `$` prefix
28+
- **Pipeline operator** creates a readable left-to-right flow of data
29+
- **Higher-order functions** allow applying operations across collections easily
30+
31+
In just a few lines of code, F# provides a clean, readable implementation that would require significantly more boilerplate in many other languages. This expressive style becomes even more valuable as your programs grow in complexity.

0 commit comments

Comments
 (0)