Dart and Functional Programming: applying Haskell concepts with dartz
Tags: flutter, dart, functional, haskell
Published: 2022-01-03 12:47:34

Haskell is a purely functional programming, with a lot of powerful features. In the other hand, Dart is a language focused on creating fast multiplatform apps.

But how could we bring some good features and concepts from Haskell into the Dart language? This is exactly what _dartz_ tries to do.

## Maybe (or Option)

The first concept we will try to apply in Dart is `Maybe`, a data structure in Haskell that can hold `Nothing` or `Just <value>`. In Haskell, `Maybe` would be used in a context where the return value of a function will not necessarily be a meaningful value.

Example:

```hs
classifyAge :: Int -> Maybe String
classifyAge age | age < 0   = Nothing
                | age < 13  = Just "Child"
                | age < 20  = Just "Teenager"
                | age < 65  = Just "Adult"
                | otherwise = Just "Elderly"
```


With _dartz_, we would use the `Option` type to have the same behavior:

```dart
Option<String> classifyAge(int age) {
  if (age < 0) return none();
  if (age < 13) return some('Child');
  if (age < 18) return some('Teenager');
  if (age < 65) return some('Adult');
  return some('Elderly');
}
```

But why you would use such a type in Dart? Because mapping values and dealing with edge case turns to be much easier:

```dart
classifyAge(12)
  .map((classification) => 'This person is a $classification')
  .getOrElse(() => 'Invalid age');

// This person is a Child
```

## Either

`Either` is another very important data structure in Haskell, because it can express two different types in terms of `Left` and `Right`.

Example:

```hs
safeDiv :: Int -> Int -> Either String Int
safeDiv x y | y == 0    = Left "Division by zero"
            | otherwise = Right (x `div` y)
```

Here, we used `Either` to express a type of value that can be either an error or an actual return value.

In Dart, we can use this to handle cases like an API call, where the left value will be an error message (or the error object itself) and the right value will be the data fetched:

```dart
Future<Either<String, Data>> getPost(int id) async {
  try {
    final response = await http.get('$url/$id');
    return right(response.data);
  } catch (e) {
    return left('Failed to fetch post');
  }
}

...

Widget build(BuildContext context) {
  return FutureBuilder<Either<String, Data>>(
    future: getPost(1),
    builder: (context, snapshot) {
      if (snapshot.hasData) {
        return snapshot.data.fold(
          (error) => Text(error),
          (post) => Text('Post: ${post.title}'),
        );
      }

      return CircularProgressIndicator();
    },
  );
}
```

## Traversable

Let's build a simple function that returns the RNA transcription of a DNA, but when a character is invalid, it returns just the character.

In Haskell, we can build this using the `Traversable` data structure, that implements the `sequenceA` function:

```hs
toRna :: String -> Either Char String
toRna dna = sequenceA $ map transcribe dna
 where
  dnaToRna = zip "GCTA" "CGAU"
  transcribe c = maybe (Left c) Right $ lookup c dnaToRna
```

In this case, `sequenceA` receives a list of `Either`s, and concatenate them in an `Either` of a list. But an interesting behavior of this function is that, when a `Left` value is found, it is returned immediately.

In Dart, we can do everything we did in Haskell, because _dartz_ implements all the functions needed:

```dart
Either<String, String> toRna(String dna) {
  final dnaToRna = IMap.fromIterables(
    ['G', 'C', 'T', 'A'],
    ['C', 'G', 'A', 'U'],
    StringOrder,
  );

  Either<String, String> transcribe(String c) => dnaToRna.get(c).fold(
        () => left(c),
        (n) => right(n),
      );

  final transcribedDna = IList.from(dna.split('')).map(transcribe);

  return IList.sequenceEither(transcribedDna).map(
    (dna) => dna.toList().join(''),
  );
}

...

print(toRNA('GCTA')); // Right(CGAU)
print(toRNA('CGAU')); // Left(U)
```
## Conclusion

The _dartz_ package offers a lot more functions and data structures than those mentioned here. If you have a functional programming background, programming in Dart can be much more intuitive with this package. But if you don't, data structures like `Option` and `Either` can be still very useful.

Some useful links:
- [dartz documentation](https://pub.dev/documentation/dartz/latest/)
- [dartz examples](https://github.com/spebbe/dartz/tree/master/example)