Skip to content

Add nameof and interpolated strings articles for F# #21542

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 7 commits into from
Nov 16, 2020
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
99 changes: 99 additions & 0 deletions docs/fsharp/language-reference/interpolated-strings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: Interpolated strings
description: Learn about interpolated strings, a special form of string that allows you to embed F# expressions directly inside them.
ms.date: 11/12/2020
---

# Interpolated strings

Interpolated strings are [strings](strings.md) that allow you to embed F# expressions into them. They are helpful in a wide range of scenarios where the value of a string may change based on the result of a value or expression.

## Syntax

```fsharp
$"string-text {expr}"
$"string-text %format-specifier{expr}"
$"""string-text {"embedded string literal"}"""
```

## Remarks

Interpolated strings let you write code in "holes" inside of a string literal. Here's a basic example:

```fsharp
let name = "Phillip"
let age = 30
printfn $"Name: {name}, Age: {age}"

printfn $"I think {3.0 + 0.14} is close to {System.Math.PI}!"
```

The contents in between each `{}` brace pair can be any F# expression.

To escape a `{}` brace pair, write two of them like so:

```fsharp
let str = $"A pair of braces: {{}}"
// "A pair of braces: {}"
```

## Typed interpolated strings

Interpolated strings can also have F# format specifiers to enforce type safey.

```fsharp
let name = "Phillip"
let age = 30

printfn $"Name: %s{name}, Age: %d{age}"

// Error: type mismatch
printfn $"Name: %s{age}, Age: %d{name}"
```

In the previous example, the code mistakenly passes the `age` value where `name` should be, and vice/versa. Because the interpolated strings use format specifiers, this is a compile error instead of a subtle runtime bug.

All format specifiers covered in [plaintext formatting](plaintext-formatting.md) are valid inside of an interpolated string.

## Verbatim interpolated strings

F# supports verbatim interpolated strings with triple quotes so that you can embed string literals.

```fsharp
let age = 30

printfn $"""Name: {"Phillip"}, Age: %d{age}"""
```

## Aligning expressions in interpolated strings

You can left-align or right-align expressions inside interpolated strings with `|` and a specification of how many spaces. The following interpolated string aligns the left and right expressions to the left and right, respectively, by 7 spaces.

```fsharp
printfn $"""|{"Left",-7}|{"Right",7}|"""
// |Left | Right|
```

## Interpolated strings and `FormattableString` formatting

You can also apply formatting that adheres to the rules for <xref:System.FormattableString>:

```fsharp
let speedOfLight = 299792.458
printfn $"The speed of light is {speedOfLight:N3} km/s."
// "The speed of light is 299,792.458 km/s."
```

Additionally, an interpolated string can also be typechecked as a <xref:System.FormattableString> via a type annotation:

```fsharp
let frmtStr = $"The speed of light is {speedOfLight:N3} km/s." : FormattableString
// Type: FormattableString
// The speed of light is 299,792.458 km/s.
```

Note that the type annotation must be on the interpolated string expression itself. F# does not implicitly convert an interpolated string into a <xref:System.FormattableString>.

## See also

* [Strings](strings.md)
90 changes: 90 additions & 0 deletions docs/fsharp/language-reference/nameof.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
title: Nameof
description: Learn about the nameof operator, a metaprogramming feature that allows you to produce the name of any symbol in your source code.
ms.date: 11/12/2020
---

# Nameof

The `nameof` expression produces a string constant that matches the name in source for nearly any F# construct in source.

## Syntax

```fsharp
nameof symbol
nameof<'TGeneric>
```

## Remarks

`nameof` works by resolving the symbol passed to it and produces the name of that symbol as it is declared in your source code. This is useful in various scenarios, such as logging, and protects your logging against changes in source code.

```fsharp
let months =
[
"January"; "February"; "March"; "April";
"May"; "June"; "July"; "August"; "September";
"October"; "November"; "December"
]

let lookupMonth month =
if (month > 12 || month < 1) then
invalidArg (nameof month) ($"Value passed in was %d{month}.")

months.[month-1]

printfn "%s" (lookupMonth 12)
printfn "%s" (lookupMonth 1)
printfn "%s" (lookupMonth 13)
```

The last line will throw an exception and `"month"` will be shown in the error message.

You can take a name of nearly every F# construct:

```fsharp
module M =
let f x = nameof x

printfn $"{(M.f 12)]}"
printfn $"{(nameof M)}"
printfn $"{(nameof M.f)}"
```

`nameof` is not a first-class function and cannot be used as such. That means it cannot be partially applied and values cannot be piped into it via F# pipeline operators.

## Nameof on operators

Operators in F# can be used in two ways, as an operator text itself, or a symbol representing the compiled form. `nameof` on an operator will produce the name of the operator as it is declared in source. To get the compiled name, use the compiled name in source:

```fsharp
nameof(+) // "+"
nameof op_Addition // "op_Addition"
```

## Nameof on generics

You can also take a name of a generic type parameter, but the syntax is different:

```fsharp
let f<'a> () = nameof<'a>
f() // "a"
```

`nameof<'TGeneric>` will take the name of the symbol as defined in source, not the name of the type substituted at a call site.

The reason why the syntax is different is to align with other F# intrinsic operators like `typeof<>` and `typedefof<>`. This makes F# consistent with respect to operators that act on generic types and anything else in source.

## Nameof in pattern matching

The [`nameof` pattern](pattern-matching.md#nameof-pattern) lets you use `nameof` in a pattern match expression like so:

```fsharp
let f (str: string) =
match str with
| nameof str -> "It's 'str'!"
| _ -> "It is not 'str'!"

f "str" // matches
f "asdf" // does not match
```
19 changes: 18 additions & 1 deletion docs/fsharp/language-reference/pattern-matching.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Pattern Matching
description: Learn how patterns are used in F# to compare data with logical structures, decompose data into constituent parts, or extract information from data.
ms.date: 08/15/2020
ms.date: 11/12/2020
---
# Pattern Matching

Expand Down Expand Up @@ -41,6 +41,7 @@ Supported patterns are shown in the following table. At run time, the input is t
|Pattern together with type annotation|*pattern* : *type*|`a : int`|
|Type test pattern|:? *type* [ as *identifier* ]|`:? System.DateTime as dt`|
|Null pattern|null|`null`|
|Nameof pattern|*nameof expr*|`nameof str`|

## Constant Patterns

Expand Down Expand Up @@ -209,6 +210,22 @@ The following example uses the null pattern and the variable pattern.

[!code-fsharp[Main](~/samples/snippets/fsharp/lang-ref-2/snippet4817.fs)]

## Nameof pattern

The `nameof` pattern matches against a string when its value is equal to the expression that follows the `nameof` keyword. for example:

```fsharp
let f (str: string) =
match str with
| nameof str -> "It's 'str'!"
| _ -> "It is not 'str'!"

f "str" // matches
f "asdf" // does not match
```

See the [`nameof`](nameof.md) operator for information on what you can take a name of.

## See also

- [Match Expressions](match-expressions.md)
Expand Down
4 changes: 4 additions & 0 deletions docs/fsharp/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
href: language-reference/unit-type.md
- name: Strings
href: language-reference/strings.md
- name: Interpolated strings
href: language-reference/interpolated-strings.md
- name: Tuples
href: language-reference/tuples.md
- name: F# Collection Types
Expand Down Expand Up @@ -248,6 +250,8 @@
href: language-reference/byrefs.md
- name: Reference Cells
href: language-reference/reference-cells.md
- name: nameof
href: language-reference/nameof.md
- name: Compiler Directives
href: language-reference/compiler-directives.md
- name: Compiler Options
Expand Down