4

I would like write a function which turns a function (a -> b) into a function (s -> t) with the help of a Lens s t a b (edit: I realised that this function already exists with Setter s t a b and is called over or %~, but it doesn't answer the question below of using a Lens to get a value). It seems simple, but I'm getting a confusing type error. To make an even more minimal example, consider the following function which simply returns the value extracted from the second argument by the lens:

f :: Lens s t a b -> s -> a
f l s = s ^. l

This doesn't compile. There are 2 errors, both in the second argument of ^. (namely l):

  • Couldn't match type 't' with 's'
  • Couldn't match type 'a' with 'b'

However, the following compiles:

f :: Getter s a -> s -> a
f l s = s ^. l

Then, I realised that in the hierarchy of lens types, the arrow between Lens and Getter specifies s=t, a=b. Is there no way to use a general Lens s t a b to get a value of type a from a value of type s?

Guillaume Chérel
  • 1,478
  • 8
  • 17

2 Answers2

5

The problem with using ^. isn't its implementation, but its type signature. If we lift its definition, we can use it for f:

f :: Lens s t a b -> s -> a 
f l s = getConst $ l Const s

This is most easily understood if you expand the definition of Lens : forall (f :: * -> *). Functor f => (a -> f b) -> s -> f t.

rampion
  • 87,131
  • 49
  • 199
  • 315
4

There is a way.

f :: Lens s t a b -> s -> a
f l s = getConst (l Const s)

This is also exactly the definition of (^.). I'm not sure why the type of (^.) is restricted in this way, possibly just for simplicity.

Reid Barton
  • 14,951
  • 3
  • 39
  • 49
  • I suspect the more generic signature makes type inference harder and thus necessitates some ugly signatures that `^.` doesn't require. – leftaroundabout Nov 27 '15 at 17:17