# Any easy way to do monad transformation in Elm?

**URL:** <https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462>\
**Category:** Learn\
**Created:** [January 4, 2018, 4:28am UTC](https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462 "2018-01-04T04:28:18Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![harfangk](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/harfangk/32/413_2.png) [@harfangk](https://discourse.elm-lang.org/u/harfangk)\
**Post date:** [January 4, 2018, 4:28am UTC](https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462/1 "2018-01-04T04:28:18Z")

</div>

I was working with elm-test lately and had to do something similar to monad transformation while building generators for my property tests. It looks like this:

```auto
fields : List (Generator String)

combinedFields : Generator (List String)
combinedFields = List.foldr (\x acc -> Random.andMap acc (Random.map (::) x)) (Random.constant []) fields

```

As you can see, it transforms `List (Generator String)` to `Generator (List String)`, and the code is a disastrous mess. I think there must be a better way to do this kind of thing - can someone tell me how to do that?

---

<div class="post-metadata">

**Author:** ![ilias](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ilias/32/6_2.png) [@ilias](https://discourse.elm-lang.org/u/ilias)\
**Post date:** [January 4, 2018, 8:18am UTC](https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462/2 "2018-01-04T08:18:29Z")

</div>

```elm
combine : List (Generator a) -> Generator (List a)
combine generators =
    List.foldr (Random.map2 (::))
        (Random.constant [])
        generators

```

I don’t think this is a disastrous mess. It’s explicit and only works for Generators and Lists, but that’s pretty typical in Elm: write the code you need. That said, I’m fairly certain that packages which already define a `Random.andMap` will typically also have a `combine`. For example [http://package.elm-lang.org/packages/elm-community/random-extra/2.0.0/Random-Extra#combine](http://package.elm-lang.org/packages/elm-community/random-extra/2.0.0/Random-Extra#combine)

`Random.Pcg` doesn’t appear to come with that included, however a similar random-extra package exists that does offer that function.

Given the above function, whether you define it yourself or get it from a package, your example becomes this: `combinedFields = combine fields`

I think the takeaway it that composing more complex definitions out of simpler ones leads to more reusability and more maintainable code. This might not answer the question in the topic, but I do feel like it offers a decent answer to the question in the post itself: how do I combine a list of generators into a generator for a list.

---

<div class="post-metadata">

**Author:** ![rtfeldman](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rtfeldman/32/50_2.png) [@rtfeldman](https://discourse.elm-lang.org/u/rtfeldman)\
**Post date:** [January 4, 2018, 10:32am UTC](https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462/3 "2018-01-04T10:32:04Z")

</div>

> [@harfangk](#):
>
> I think there must be a better way to do this kind of thing

We’re really big on avoiding [the XY problem](http://www.perlmonks.org/index.pl?node_id=542341), so I think the best way to pursue a better way to write these generators would be to look at the generators themselves.

Could you post the code for some of the generators? (As in, some of the logic which is currently calling `combinedFields`.)

---

<div class="post-metadata">

**Author:** ![ericgj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ericgj/32/85_2.png) [@ericgj](https://discourse.elm-lang.org/u/ericgj)\
**Post date:** [January 4, 2018, 6:31pm UTC](https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462/4 "2018-01-04T18:31:07Z")

</div>

There’s also `Task.sequence` you can look at for a recursive implementation.

---

<div class="post-metadata">

**Author:** ![harfangk](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/harfangk/32/413_2.png) [@harfangk](https://discourse.elm-lang.org/u/harfangk)\
**Post date:** [January 5, 2018, 12:27am UTC](https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462/5 "2018-01-05T00:27:24Z")

</div>

@ilias  
I’ve looked into a few `combine` functions as per your suggestion and they all seem to use similar pattern. I was hoping for something that looks less daunting because I would have to explain how that function works to someone with zero experience in functional programming, but I guess it will have to do. Thank you very much.

@rtfeldman  
You are somewhat correct in this being a case of XY problem. I was generating Json.Encode.Value for a dictionary of records to test a Json.Decode.Decoder and the generator was like 40 lines long, so in the end I scrapped the entire section for a simpler approach and it works great. By the time I was asking this question, I was genuinely curious about how to express monadic transformation in a simpler way.

ericgj  
That recursive implementation is quite interesting! Thank you for pointing me toward it.

---

<div class="post-metadata">

**Author:** ![rtfeldman](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rtfeldman/32/50_2.png) [@rtfeldman](https://discourse.elm-lang.org/u/rtfeldman)\
**Post date:** [January 5, 2018, 12:50am UTC](https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462/6 "2018-01-05T00:50:46Z")

</div>

> [@harfangk](#):
>
> in the end I scrapped the entire section for a simpler approach and it works great. By the time I was asking this question, I was genuinely curious about how to express monadic transformation in a simpler way.

A common thing I’ve noticed for folks coming to Elm from Haskell is that in many cases, trying to do things in a familiar Haskelly way leads to a lot of frustration.

There are a great many design decisions where Haskell went one way and Elm very intentionally went another, and it often takes a conscious mindset shift to avoid reaching for familiar-but-inapplicable approaches out of habit!

---

<div class="post-metadata">

**Author:** ![ilias](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ilias/32/6_2.png) [@ilias](https://discourse.elm-lang.org/u/ilias)\
**Post date:** [January 5, 2018, 8:21am UTC](https://discourse.elm-lang.org/t/any-easy-way-to-do-monad-transformation-in-elm/462/7 "2018-01-05T08:21:57Z")

</div>

> [@harfangk](#):
>
> I was hoping for something that looks less daunting because I would have to explain how that function works to someone with zero experience in functional programming

Let’s try and explain how we can arrive at that implementation using intuitions and some minor perusal of the API for List and Random!

## Step 1

Realize we want to “transform” a list of values, into some other thing. If the “other thing” is a new `List`, you would generally use `List.map`. Let’s assume we don’t really want to change the generators themselves, so let’s start with that:

```elm
combine generators =
    List.map identity generators

```

## Step 2

Hm, the expected result isn’t “just” a `List`, though, which is all that `List.map` is capable of generating. We do have a slightly more flexible function in our arsenal, though - folding. Whichever way we fold, we can use it to create something else from a list by using a fold. Since our final type does have `List` in its result type, and we want the generators to be in the same order as we feed them in, let’s start by rewriting the previous attempt using a fold:

```elm
combine generators =
    List.foldr (\item acc -> item :: acc) [] generators

```

Oh, that lambda looks like it’s just the definition of the cons operator, so let’s change that slightly. Generally being super concise isn’t worth the trouble, but I feel like this is a pretty acceptable use of partial application for infix operators:

```elm
combine generators =
    List.foldr (::) [] generators

```

## Step 3

Okay, the type signature doesn’t match up - we’re returning a `List` rather than a `Generator (List a)`. So, in our fold, rather than adding to a List, we want to add to a `Generator (List a)`.

I generally find it helpful to get the “initial accumulator” figured out before figuring out the function to apply to it. In this case, we want the final result to be a `Generator (List a)` so we want out initial accumulator to be of that type.

So, how can we create an Generator for an empty list as our initial accumulator? Ah, there’s a `constant` function. Cool, let’s try that. Since we’re now working on getting the type signature to work out, let’s add that in, too:

```elm
combine : List (Generator a) -> Generator (List a)
combine generators =
    List.foldr (::) (Random.constant []) generators

```

## Step 4

So, we have a type mismatch for our accumulating function. We’re giving it a function `a -> List a -> List a` but it’s expecting a function `Generator a -> Generator (List a) -> Generator (List a)`. So the goal is to find a way to change that first one into that second one.

When looking for a function, it helps to go look for a function that has a generalized form of the specific thing you’re attempting to do.

In the most general terms, we’re looking for something that takes a `a -> b -> c` and gives us a `Generator a -> Generator b -> Generator c`.

Turns out there’s a `Random.map2 : (a -> b -> c) -> (Generator a -> Generator b -> Generator c)`. Lucky us!

```elm
combine : List (Generator a) -> Generator (List a)
combine generators =
    List.foldr (Random.map2 (::)) (Random.constant []) generators

```

* * *

Granted, that explanation isn’t great for someone with absolutely zero experience with FP. However, I think it’s possible to explain it using some of the intuitions that people build up while working with functional programming, which to me seems a bunch simpler than saying “it’s monad transformation”. Of course, different people learn in different ways, so your mileage may vary!
