如何简化将多态字段名称的字段调用为一个类型类型 [英] How to simplify calling a field on a polymorphic field name into one typeclass

查看:133
本文介绍了如何简化将多态字段名称的字段调用为一个类型类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上一个问题中,我问过在使用DuplicateRecordField时如何使记录字段变成多态。我从 @ user2407038 得到了很好的答案。他回答了我的初始规范提供的每个领域的类型类,但他提到可以将它们全部简化为一个类型类。


(注意:这也可以推广到单个类,并且有一个与字段名称相对应的附加参数;这可能超出了这个问题的范围)。


我不确定如何去做这个泛化。有人对此有何看法?

解决方案

定义这样的类很容易

   -  s有一个名为field的字段并将其设置为b将s变成
class HasLens field stab |字段s - > a,字段t - > b,字段s b - > t,场t a - > s其中
- Fundeps是很常识的,并且也出现在评论链接的库中
lensOf :: Functor f => (a - > f b) - > s - > ft
- 不知道为什么上面链接的库包含头文件中的f ...

您会注意到字段没有出现在 lensOf 的类型中,所以此类将不可用,因为推理者永远无法弄清楚它应该是什么。您有以下选项:

旧:

  class HasLens name stab | ...其中
lensOf :: Functor f =>代理名称 - > (a - > f b) - > s - > f t
- 或者没有运行时开销的Proxy#,或者所有代理。 Functor f =>代理名称 - > ...

代理参数是一个假;除了告诉编译器关于 name 的信息之外,它绝不会用于任何其他用途。不过,用法是难以忍受的:

$ p $ lensOf(Proxy :: Proxyfield)
- 或proxy# ,或未定义

新:

<$ p $ { - #LANGUAGE AllowAmbiguousTypes,TypeApplications# - }

显式类型应用程序在调用站点上设置 name (同时确保 name

  lensOf @field


  { - #b 


< LANGUAGE AllowAmbiguousTypes
,DataKinds
,FlexibleContexts
,FlexibleInstances
,FunctionalDependencies
,NoMonomorphismRestriction
,PolyKinds
,ScopedTypeVariables
,TypeApplications
# - }

导入Control.Lens

类HasLens xstab | x s - > a,x t - > b,x s b - > t,x t a - > s其中
lensOf :: Functor f => (a - > f b) - > s - > ft

data Tup2 ab = Tup2 {_left2 :: a,_right2 :: b}导出显示
数据Tup3 abc = Tup3 {_left3 :: a,_middle3 :: b,_right3 :: c}派生Show

实例HasLensleft(Tup2 ab)(Tup2 a'b)a a'where
lensOf = lens _left2 $ \tx - > t {_left2 = x}

实例HasLensleft(Tup3 a b c)(Tup3 a'b c)a a'其中
lensOf = lens _left3 $ \ t x - > t {_left3 = x}

实例HasLensright(Tup2 a b)(Tup2 a b')b b'其中
lensOf = lens _right2 $ \ t x - > t {_right2 = x}

实例HasLensright(Tup3 a b c)(Tup3 a b c')c c'其中
lensOf = lens _right3 $ \ t x - > t {_right3 = x}

swap':: forall xlr xrl l x xr xrr。 (HasLensleftxlr xrr l,HasLensrightxlr xll r l,HasLensleftxll xrl l r,HasLensrightxrr xrl r l)=> xlr - > xrl
swap'x = x& lensOf @left。〜x ^#lensOf @right@xlr @xll @r @l
&镜头@right。〜x ^#lensOf @left@xlr @xrr @l @r

main =做出$ Tup2 5 6
出$ Tup3'l'' m''r'
out $ Tup2l'r'
out $ Tup3 17 [5,10]a
where out = print。交换'


In a previous question I asked how a record field can be made polymorphic when using DuplicateRecordFields. I got an excellent answer for this from @user2407038. He answered the question to my initial spec providing one type class per field, but he mentioned that it could all be simplified into one typeclass.

(Note: this too can be generalized to a single class with an additional parameter corresponding to the field name; this is probably outside the scope of this question).

I'm not sure how to go about doing this generalization. Does anybody have any ideas on how this can accomplished?

解决方案

Defining such a class is easy enough

-- s has a field named field of type a and setting it to b turns the s into a t
class HasLens field s t a b | field s -> a, field t -> b, field s b -> t, field t a -> s where
  -- Fundeps are pretty common sense, and also appear in the library linked in the comments
  lensOf :: Functor f => (a -> f b) -> s -> f t
  -- Not sure why the library linked above includes f in the class head...

You'll notice that field appears nowhere in lensOf's type, so this class would be unusable as is, because the inferencer can never figure out what it should be. You have these options:

Old:

class HasLens name s t a b | ... where
  lensOf :: Functor f => Proxy name -> (a -> f b) -> s -> f t
  -- Or Proxy#, which has no runtime overhead, or forall proxy. Functor f => proxy name -> ...

The Proxy argument is a dummy; it is never used for anything except telling the compiler about name. Usage is unbearably ugly, though:

lensOf (Proxy :: Proxy "field")
-- or proxy#, or undefined

New:

{-# LANGUAGE AllowAmbiguousTypes, TypeApplications #-}

Now you use explicit type applications to set name at the call site (also make sure that name is first in the class head, or else the order of type arguments will get messed up).

lensOf @"field"

Fuller example:

{-# LANGUAGE AllowAmbiguousTypes
           , DataKinds
           , FlexibleContexts
           , FlexibleInstances
           , FunctionalDependencies
           , NoMonomorphismRestriction
           , PolyKinds
           , ScopedTypeVariables
           , TypeApplications
#-}

import Control.Lens

class HasLens x s t a b | x s -> a, x t -> b, x s b -> t, x t a -> s where
  lensOf :: Functor f => (a -> f b) -> s -> f t

data Tup2 a b = Tup2 { _left2 :: a, _right2 :: b } deriving Show
data Tup3 a b c = Tup3 { _left3 :: a, _middle3 :: b, _right3 :: c } deriving Show

instance HasLens "left" (Tup2 a b) (Tup2 a' b) a a' where
  lensOf = lens _left2 $ \t x -> t { _left2 = x }

instance HasLens "left" (Tup3 a b c) (Tup3 a' b c) a a' where
  lensOf = lens _left3 $ \t x -> t { _left3 = x }

instance HasLens "right" (Tup2 a b) (Tup2 a b') b b' where
  lensOf = lens _right2 $ \t x -> t { _right2 = x }

instance HasLens "right" (Tup3 a b c) (Tup3 a b c') c c' where
  lensOf = lens _right3 $ \t x -> t { _right3 = x }

swap' :: forall xlr xrl l r xll xrr. (HasLens "left" xlr xrr l r, HasLens "right" xlr xll r l, HasLens "left" xll xrl l r, HasLens "right" xrr xrl r l) => xlr -> xrl
swap' x = x & lensOf @"left"  .~ x^#lensOf @"right" @xlr @xll @r @l
            & lensOf @"right" .~ x^#lensOf @"left"  @xlr @xrr @l @r

main = do out $ Tup2 5 6
          out $ Tup3 'l' 'm' 'r'
          out $ Tup2 "l" 'r'
          out $ Tup3 17 [5,10] "a"
  where out = print . swap'

这篇关于如何简化将多态字段名称的字段调用为一个类型类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆