用镜头替换记录投影功能

Ale*_*lec 4 haskell ghc template-haskell

几乎每次我做一个记录,我发现自己后来立即添加makeLenses ''Record(从镜头),我从来没有真正使用记录给我的投影功能.事实上,看看makeLenses产生什么(使用GHC -ddump-splices标志),看起来甚至不使用那些投影功能,除了为它产生的镜头选择一个名称.

有没有办法,通过TemplateHaskell或通过预处理器,或坦率地任何其他魔法,我可以得到记录投影功能直接Van Laarhoven镜头?

要明确,这意味着

data Record a b = Record { x :: a, y :: b }
Run Code Online (Sandbox Code Playgroud)

会生成(带type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t)

x :: forall a b c. Lens (Record a b) (Record c b) a c
x f (Record _x _y) = fmap (\x' -> Record x' _y) (f _x)

y :: forall a b c. Lens (Record a b) (Record a c) b c
y f (Record _x _y) = fmap (\ y' -> Record _x y') (f _y)
Run Code Online (Sandbox Code Playgroud)

代替

x :: forall a b. Record a b -> a
x (Record _x _y) = _x

y :: forall a b. Record a b -> b
y (Record _x _y) = _y
Run Code Online (Sandbox Code Playgroud)

它不仅可以摆脱样板makeLenses,还可以释放命名空间(因为不会定义投影函数).

这是一件小事,但由于它附在我的所有记录上,并且记录并不是那么罕见,它真的开始让我神经紧张......

hao*_*hao 5

有一个名为OverloadedRecordFields/MagicClasses的GHC扩展提议.Adam Gundry正在努力提出积极的拉动请求.它与OverloadedRecordLabels结合使用,旨在解决这个问题!

data Foo = Foo { x :: Int, y :: Int }

class IsLabel (x :: Symbol) a where
  fromLabel :: Proxy# x -> a
Run Code Online (Sandbox Code Playgroud)

使用类似的示例数据类型Foo,表达式#x中的子表达式#x (foo :: Foo) 将被编译器神奇地扩展为fromLabel @"x" @Foo proxy#.@符号,类型应用符号,是另一个GHC 8-ism.

与此不同x,#x可以修改行为以满足您的需求.你可能只是一个常规的投影功能.与OverloadedLabels启用,我们已经进入一个多态的投影功能getField:

instance HasField name big small => IsLabel name (big -> small) where
  fromLabel proxy big = getField proxy big
Run Code Online (Sandbox Code Playgroud)

或者我们可以用刺式镜片满足约束:

instance ( Functor f
         , HasField name big small
         , UpdateField name big big' small') =>
         IsLabel name ((small -> f small') -> (big -> big')) where
  fromLabel proxy f big =
    setField proxy big <$> f (getField proxy big)
Run Code Online (Sandbox Code Playgroud)

有了这样的例子,你可以立即开始使用#x镜头:

over #x (* 2) (Foo 1008 0) -- evaluates to Foo 2016 0
Run Code Online (Sandbox Code Playgroud)