# tryCreate

Namespace: `FsToolkit.ErrorHandling`

## Function Signature

```fsharp
string -> 'a -> Result<^b, (string * 'c)>
```

`^b` is a [statically resolved parameter](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/statically-resolved-type-parameters) with the below constraint

```fsharp
^b : (static member TryCreate : 'a -> Result< ^b, 'c>)
```

This can be useful when constructing types for collecting construction result errors associated with passed-in parameter names, as the example below demonstrate.

## Examples

### Example 1

[Making illegal states unrepresentable](https://fsharpforfunandprofit.com/posts/designing-with-types-making-illegal-states-unrepresentable/) is a common practice in F#. A common way to do it is to have a type, say `MyType`, with a private constructor and a `TryCreate` member that returns a `Result<MyType, 'a>`, like shown below:

```fsharp
type Longitude = private Longitude of float with
  member this.Value = let (Longitude lng) = this in lng

  // float -> Result<Longitude, string>
  static member TryCreate (lng : float) =
    if lng >= -180. && lng <= 180. then
      Ok (Longitude lng)
    else
      Error $"%A{lng} is a invalid longitude value"
```

Let's assume that we have few more similar types as below

```fsharp
type Latitude = private Latitude of float with
  member this.Value =
    let (Latitude lat) = this
    lat

  // float -> Result<Latitude, string>
  static member TryCreate (lat : float) =
    if lat >= -90. && lat <= 90. then
      Ok (Latitude lat)
    else
      Error $"%A{lat} is a invalid latitude value"

open System

type Tweet = private Tweet of string with
  member this.Value =
    let (Tweet tweet) = this in tweet

  static member TryCreate (tweet : string) =
    if String.IsNullOrEmpty tweet then
      Error "Tweet shouldn't be empty"
    elif tweet.Length > 280 then
      Error "Tweet shouldn't contain more than 280 characters"
    else Ok (Tweet tweet)
```

Assume furthermore that the types above are used in the following types:

```fsharp
type Location = {
  Latitude : Latitude
  Longitude : Longitude
}

type CreatePostRequest = {
  Tweet : Tweet
  Location : Location
}
```

And that we have the following functions to create these composed types:

```fsharp
let location lat lng =
  {Latitude = lat; Longitude = lng}

let createPostRequest lat long tweet =
  {Tweet = tweet; Location = location lat long}
```

And the following DTO types:

```fsharp
type LocationDto = {
  Latitude : float
  Longitude : float
}

type CreatePostRequestDto = {
  Tweet : string
  Location : LocationDto
}
```

We can then do result using `Result.tryResult` and the [`Result` infix operators](/fstoolkit-errorhandling/fstoolkit.errorhandling/result/operators.md) as below:

```fsharp
open FsToolkit.ErrorHandling
open FsToolkit.ErrorHandling.Operator.Validation

// CreatePostRequestDto -> Result<CreatePostRequest, (string * string) list>
let validateCreatePostRequest (dto : CreatePostRequestDto) =
  createPostRequest
  <!^> Result.tryCreate "latitude" dto.Location.Latitude
  <*^> Result.tryCreate "longitude" dto.Location.Longitude
  <*^> Result.tryCreate "tweet" dto.Tweet
```

Here the types of the `Result.tryCreate` lines are inferred, and the types' `TryCreate` member is used to construct them.

```fsharp
validateCreatePostRequest
  {Tweet = ""; Location = {Latitude = 300.; Longitude = 400.}}
// Error
//   [("latitude", "300.0 is a invalid latitude value")
//    ("longitude", "400.0 is a invalid longitude value")
//    ("tweet", "Tweet shouldn't be empty")]
```

These errors can then for example be returned in an API response:

```fsharp
validateCreatePostRequest {Tweet = ""; Location = {Latitude = 300.; Longitude = 400.}}
|> Result.mapError Map.ofList
// Map<string, string>
```

When serialized:

```json
{
  "latitude": "300.0 is a invalid latitude value",
  "longitude": "400.0 is a invalid longitude value",
  "tweet": "Tweet shouldn't be empty"
}
```

### Example 2

In Example 1, we collected all the error messages. But what if we wanted to stop on the first error? One way to do this is to make use of the `result` computation expression instead of using infix operators from `Result` module.

```fsharp
// CreatePostRequestDto -> Result<CreatePostRequest, string * string>
let validateCreatePostRequest (dto : CreatePostRequestDto) = result {
  let! t = Result.tryCreate "tweet" dto.Tweet
  let! lat = Result.tryCreate "latitude" dto.Location.Latitude
  let! lng = Result.tryCreate "longitude" dto.Location.Longitude
  return createPostRequest lat lng t
}
```

### Example 3

In the examples above, we assume that a location is always required for creating a post. Let's assume that the requirement is changed and now the location is optional:

```fsharp
type CreatePostRequestDto = {
  Tweet : string
  Location : LocationDto option
}

type CreatePostRequest = {
  Tweet : Tweet
  Location : Location option
}

let createPostRequest location tweet =
  {Tweet = tweet; Location = location}
```

Then `validateCreatePostRequest` can be rewritten using the [Option.traverseResult](https://github.com/demystifyfp/FsToolkit.ErrorHandling/blob/master/gitbook/option/traverseResult.md) function as below:

```fsharp
open FsToolkit.ErrorHandling
open FsToolkit.ErrorHandling.Operator.Validation

let validateLocation (dto : LocationDto) =
  location
  <!^> Result.tryCreate "latitude" dto.Latitude
  <*^> Result.tryCreate "longitude" dto.Longitude

let validateCreatePostRequest (dto : CreatePostRequestDto) =
  createPostRequest
  <!> Option.traverseResult validateLocation dto.Location
  <*^> Result.tryCreate "tweet" dto.Tweet
```

Note: We are using the `<!>` operator in the `validateCreatePostRequest` instead of `<!^>` operator as the right side result is returning a list of errors (`Result<Location option, (string * string) list>`).

```fsharp
validateCreatePostRequest
  {Tweet = ""; Location = Some {Latitude = 300.; Longitude = 400.}}
//  Error
//    [("latitude", "300.0 is a invalid latitude value")
//     ("longitude", "400.0 is a invalid longitude value")
//     ("tweet", "Tweet shouldn't be empty")]

validateCreatePostRequest {Tweet = ""; Location = None}
//  Error [("tweet", "Tweet shouldn't be empty")]
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://demystifyfp.gitbook.io/fstoolkit-errorhandling/fstoolkit.errorhandling/result/trycreate.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
