Left Hand Side Parameters -> More than type signature

Hi,

While trying to learn lenses, ReaderT Design Pattern and Continuation Passing Style, I came across the following when I’m unsure about a parameter:

myLiftA2 :: (a -> b -> c) -> (Int -> IO a) -> (Int -> IO b) -> (Int -> IO c)
myLiftA2 f enva envb int = f <$> enva int <*> envb int
Summary

I don’t understand how the int parameter can exist on the left-side

SOLVED: The type signature is the same as:

myLiftA2 :: (a -> b -> c) -> (Int -> IO a) -> (Int -> IO b) -> Int -> IO c

so the Int was there the whole time: Without the int, we return a function that takes an int and returns an IO c → with it applied, we return an IO c :slight_smile:

I also notice this in the definition for a function to create a lens:
EDIT: expanded type of Lens

lens :: forall f. Functor f => (s -> a) -> (s -> a -> s) -> (a -> f a) -> s -> f s
lens getterFunc setterFunc sa s = setterFunc s <$> sa (getterFunc s)

where sa :: (a -> f a) and s :: s seem to appear out of nowhere on the left hand side.

EDIT2: Now given the expanded type signature this is more apparent and this one is also now solved for me!


I now have a new issue:

The type of most lens functions (getter, setter, traversal) tends to look like

type Getter/Setter s a = Functor/Applicative f => (a -> f a) -> (s -> f s)

which, in the case of getters apparently is equivalent to s -> a but not in the case of setters.

In this case a getter can be a function that has a function of (a -> f a) and a parameter s and this should return an f s so I’m not sure how we get s -> a here.

Can anyone help explain this to me?

EDITS: Sorry for the edits, posted on reddit too and slowly working things out at the same time.

Remember that -> is right associative, so a -> (b -> c) is the same as a -> b -> c.

fooSum :: Int -> (Int -> Int)
fooSum a b = a + b
-- λ> fooSum 7 2
-- 9
2 Likes

That’s a very good question. I remember this confusing me when I learned haskell.

Perhaps for the first example, it may help if I show different ways to write the same type signature

-- original from your post:
myLiftA2 :: (a -> b -> c) -> (Int -> IO a) -> (Int -> IO b) -> (Int -> IO c)

-- this one might answer where your int comes from, because we can remove some of the parentheses from above
myLiftA2 :: (a -> b -> c) -> (Int -> IO a) -> (Int -> IO b) -> Int -> IO c

-- we can also add more parentheses:
myLiftA2 :: (a -> b -> c) -> (Int -> IO a) -> ((Int -> IO b) -> (Int -> IO c))

-- go all out:
myLiftA2 :: ((a -> b -> c) -> ((Int -> IO a) -> ((Int -> IO b) -> (Int -> IO c))))

So basically, from right to left, you can add more parentheses, and it won’t change the meaning. Technically, the original function has redundant parentheses around the (Int -> IO c). The reason why they’re there, is because the author of that function wanted to say something with it: hey usually this myLiftA2 is used as something that takes three things and then returns that Int -> IO c. That last part is possible due to currying. I’ll skip explaining that as it is a bit out of scope. Ask if you like to know more about that.

1 Like

The usual way this works is that the effect you want—in this case, smuggling out an a—is accomplished via a particular choice of f.

A getter of type

forall f. (Contravariant f, Functor f) => (a -> f a) -> s -> f s

can be instantiated with f ~ Const a, where Const is a newtype that takes two type arguments, holds an a and does absolutely nothing with its second type argument. As such, it is trivially Functor and Contravariant.

So now your getter is of type

(a -> Const a a) -> s -> Const a s

Unwrapping the Consts, this is equivalent to

(a -> a) -> s -> a

Give it the identity function and you have your s -> a.

All of the van Laarhoven lens types are like this. The types constrain the sort of clever tricks you can do with f, and then the user of the lens provides a particular f that meets those constraints to get the effect you want. (Of course, if you’re using the operators from the lens API, you rarely have to do this sort of thing manually, but it’s often very helpful to know what those operators are doing anyway.)

1 Like

Oh my God. That’s pretty awesome :rofl:.

I was seeing Const about but I’ve never had it broken down like this and now it’s clicked, so thank you so much :slight_smile:

I’ll try and see if I can translate that in to figuring out this works with Identity and setters in the morning :slight_smile:

1 Like