Flying back from a nice 2 week vacation in Texas & I had a chance to think about primative operators. Specifically in this post I’ll be exploring what a primative opeartor actually is and whether there’s such a thing as “more powerful” primatives.

To get started I’ll be working with **map**a function we all hold near and dear. **Map**’s signature takes a value of some type **A** and a function **A => B**. So the full signature would be:

`def map[A, B](x: A)(f: A => B) : B`

This function has as it’s range and domain all possible morphisms across two types. For instance, based on signature alone **map** can transform an **Int** into a **Dinosaur** via some magical function **f(i: Int)**. However, what it cannot do is transform an **Int** and a **String** into a **Dinosaur**. You could accomplish this via a **Tuple[Int, String]** and an **f(t: (Int, String))** but because you are still acting on a single parameter you’re unable to partially apply **map** to the tuple. Really this means we’ve been forced into evaluating all or none of the parameters with no ability to choose between them. That seems like an unnecessary constraint for a morphism.

This suggests there exists some more “powerful” version of **map** that allows us to partially apply across multiple parameters and evaluate them independently. For lack of a better name lets call this **map2**. **Map2** accepts two types of parameters & returns a third, so we know it’s signature looks like this:

`def map2[A,B,C](a: A, b: B)(f: (A, B) => C): C`

**Map2** is able to handle anything **map** can do by providing a no-op type as **B**’s type argument. For example, here’s **map** implemented via **map2**:

```
def map[A, B](a: A)(f: A => B): B =
map2(a, NoOp)((x, y) => f(x))
```

This on it’s own buys us alot of power because we no longer need to worry about actually applying the computation for the morphism in **map**, instead we can defer it into **map2**. The next question I have about **Map2** is whether we can create a function **Map3** that is again more powerful than **Map2**? Logically it would seem that since **Map2**’s domain and range are strictly supersets of the domain and range for **Map** that it holds **Map2** should be a subset of **Map3**, right?

It turns out this isn’t quite the case on the implemetation level. You can actually implement all other arity mapping functions in terms of **Map2** by using a trick simmilar to have a right fold operates. That is to say via building up a chain of function calls to provide the proper arity. Here’s **Map3**:

```
def map3[A, B, C, D](a: A, b: B, c: C)(f: (A, B, C) => D): D =
map2(a, b)((x, y) => map2(c, NoOp)(f(a, b, c)))
```

This ends up creating a chain of map2 calls that can be composed together as **f • g • …** to generate the full chain.

As I think about it, it is actually possible to use the same approach to construct a similarly deep function call chain for **map**, allowing us to use **map** as the base primative and implement all other arity **map**’s in terms of the base primitive.

With standard Map it’s still impossible to use a single function call to support >1 parameter, where as **map** with arity > 1 inherently supports this

I’ll update this post once I’ve had a chance to do some more research on which of the two functions is actually the base primitive..