Replace record projection function with lenses

早过忘川 提交于 2019-12-05 14:04:39

There is a GHC extensions proposal called OverloadedRecordFields/MagicClasses. Adam Gundry is working on an active pull request. It, combined with OverloadedRecordLabels, are intended to address this very problem!

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

class IsLabel (x :: Symbol) a where
  fromLabel :: Proxy# x -> a

With an example datatype like Foo, the subexpression #x in the expression #x (foo :: Foo) will be magically expanded by the compiler to fromLabel @"x" @Foo proxy#. That @ symbol, the type application symbol, is another GHC 8-ism.

Unlike x, the behavior of #x can be modified to suit your needs. You might have it be just a regular projection function. With OverloadedLabels enabled, we already have access to a polymorphic projection function getField:

instance HasField name big small => IsLabel name (big -> small) where
  fromLabel proxy big = getField proxy big

Or we could satisfy the constraint with stab-style lenses:

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)

With such an instance you could immediately start using #x as a lens:

over #x (* 2) (Foo 1008 0) -- evaluates to Foo 2016 0
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!