如何在纯脚本中使用镜头在 ADT 之间进行转换?

Posted

技术标签:

【中文标题】如何在纯脚本中使用镜头在 ADT 之间进行转换?【英文标题】:How to convert between ADTs with lenses in purescript? 【发布时间】:2019-04-09 22:20:06 【问题描述】:

这是代码是我想要完成的工作但简化的示例。我想从一种类型的包装记录映射到另一种类型:

import Prelude
import Data.Lens
import Data.String as String

newtype AsString = AsString  names :: Array String 
newtype AsSize   = AsSize    names :: Array Int 

_names = lens _.names (_  names = _ )

to_sizes :: AsString -> AsSize
to_sizes (AsString s) = AsSize $ over (_names <<< traversed) String.length s

如何只使用镜头而不先打开记录?

当使用lens (\(AsString s) -&gt; s) (const AsString) 之类的镜头作为原始类型时,我猜它期望结果是原始类型?

【问题讨论】:

【参考方案1】:

我不确定您是否会对这个答案感到满意,因为我还建议在这里进行一些重构...... 如果您不满意,请告诉我,我将尝试根据以下镜头提供代码示例,而不涉及您的类型;-)

您是否可以接受如下更改AsStringAsSize

newtype Names a = Names  names :: Array a 

type AsString = Names String
type AsSize = Names Int

这种重构将稍微简化操作并使您的类型更具可重用性。我真的很推荐这个关于类型参数的力量的演讲:https://www.youtube.com/watch?v=BHjIl81HgfE)。

对于names 字段,我们可以使用通用prop 函数创建镜头。

关于Name 类型,我们首先应该派生Newtype 实例(请注意这个派生的非常具体的语法 - 我认为_ 类型编译器自己推断):

 newtype Names a = Names  names :: Array a 
 derive instance newtypeNames ∷ Newtype (Names a) _

这个类提供wrapunwrap 方法,供_Newtype 镜头使用。所以我们现在可以使用_Newtype 镜头。

最后你应该能够组合这两个。我们开始:

module Main where

import Prelude

import Data.Lens (over, traversed)
import Data.Lens.Iso.Newtype (_Newtype)
import Data.Lens.Record (prop)
import Data.Newtype (class Newtype)
import Data.String as String
import Type.Prelude (SProxy(..))

newtype Names a = Names  names :: Array a 
derive instance newtypeNames ∷ Newtype (Names a) _

type AsString = Names String
type AsSize = Names Int

_names  = prop (SProxy ∷ SProxy "names")

toSizes :: AsString -> AsSize
toSizes = over (_Newtype <<< _names <<< traversed) (String.length)

附言

如果我修改相同的类型,我也经常写这个来简化类型推断:

_Newtype' ∷ ∀ s a. (Newtype s a) ⇒ Iso' s a
_Newtype' = iso unwrap wrap

【讨论】:

谢谢。我正在考虑这样做,很高兴有一个例子,因为我今天正在玩 Genericderive instance。我还没有了解 SProxy 的功能,您在 PS 上有点迷失了我,但这对我来说是非常有价值的学习材料。 你是@DerKastellan 在 slack 频道 #purescript 或 #purescript-beginners 上注册的吗?我们可以进一步讨论SProxy - 如果你愿意,请直接联系我;-)【参考方案2】:

我想我通过实验找到了答案:

import Prelude
import Data.Lens
import Data.String as String

newtype AsString = AsString  names :: Array String 
newtype AsSize   = AsSize    names :: Array Int 

_ToSize = lens (\(AsString s) -> s) (const AsSize)
_names  = lens _.names (_  names = _ )

to_sizes :: AsString -> AsSize
to_sizes s = over (_ToSize <<< _names <<< traversed) String.length s

(const AsSize) 似乎将原始记录“镜像”到新类型中。

【讨论】:

以上是关于如何在纯脚本中使用镜头在 ADT 之间进行转换?的主要内容,如果未能解决你的问题,请参考以下文章

如何在纯 JavaScript 中切换元素的类?

如何在纯c++环境中使用qt中的图像?

如何在纯 php 中创建刷新令牌?

确保两个 (G) ADT 在 (GHC) Haskell 中具有相同的底层表示

使用Adt自带的工具进行Android自己主动化測试

尼康D型镜头手动对焦应该如何操作?