是否有像fromNewtype这样的操作的简写.F .toNewtype`?

Ign*_*rov 8 haskell coerce newtype

呈现本身往往越式安全正在通过引入的模式newtype是项目的值(或多个值)newtype包装,做一些操作,然后收回投影.一个无处不在的例子是SumProduct幺半群:

? x + y = getSum $ Sum x `mappend` Sum y
? 1 + 2
3
Run Code Online (Sandbox Code Playgroud)

我想象可以为每个函数自动推出一系列函数,如withSum,withSum2等等newtype.或者也许Identity可以创建参数化,以供使用ApplicativeDo.或许还有其他一些我无法想到的方法.

我想知道是否有一些现有技术或理论.

PS   我不满意coerce,原因有两个:

  • 安全   我认为它不是很安全.在被指出它实际上是安全的之后,我尝试了一些事情并且我无法做任何有害的事情,因为它有可能存在模糊性时需要类型注释.例如:

    ? newtype F = F Int deriving Show
    ? newtype G = G Int deriving Show
    ? coerce . (mappend (1 :: Sum Int)) . coerce $ F 1 :: G
    G 2
    ? coerce . (mappend (1 :: Product Int)) . coerce $ F 1 :: G
    G 1
    ? coerce . (mappend 1) . coerce $ F 1 :: G
    ...
        • Couldn't match representation of type ‘a0’ with that of ‘Int’
            arising from a use of ‘coerce’
    ...
    
    Run Code Online (Sandbox Code Playgroud)

    但是我仍然不欢迎coerce,因为一旦达到它的习惯性,剥去一个安全标签并射击某人就太容易了.想象一下,在加密应用程序中,有两个值:x :: Prime Intx' :: Sum Int.我宁愿打字getPrime,getSum每次使用它们,而不是coerce一切,有一天会发生灾难性的错误.

  •   对于某些操作coerce简写而言,有用性并没有带来太大的影响.我的帖子的主要例子,我在此重复:

    ? getSum $ Sum 1 `mappend` Sum 2
    3
    
    Run Code Online (Sandbox Code Playgroud)

    - 变成了这个尖刺怪物的线条:

    ? coerce $ mappend @(Sum Integer) (coerce 1) (coerce 2) :: Integer
    3
    
    Run Code Online (Sandbox Code Playgroud)

    - 这几乎没有任何好处.

dan*_*iaz 9

通过将summands放入一个列表并使用此处ala提供的函数,可以更好地处理您的"尖刺怪物"示例,该函数具有以下类型:

ala :: (Coercible a b, Coercible a' b') 
    => (a -> b) 
    -> ((a -> b) -> c -> b')   
    -> c 
    -> a' 
Run Code Online (Sandbox Code Playgroud)

哪里

  • a未打开的基本类型.
  • b是包装的新类型a.
  • a -> b 是newtype构造函数.
  • ((a -> b) -> c -> b')是一个函数,知道如何包装基类型的值a,知道如何处理类型的值c(几乎总是as 的容器)并返回包装结果b'.实际上,这个功能几乎总是如此foldMap.
  • a'展开最后的结果.展开是由ala它自己处理的.

在你的情况下,它将是这样的:

ala Sum foldMap [1,2::Integer]
Run Code Online (Sandbox Code Playgroud)

"ala"功能可以通过除了coerce例如使用泛型来处理展开或甚至透镜之外的其他方式来实现.


She*_*rsh 6

是的,有!这是包的coerce功能base.它允许自动转换newtype和转换newtype.GHC实际上在强制背后有很大一部分理论.

relude我称这个功能under.

ghci> newtype Foo = Foo Bool deriving Show
ghci> under not (Foo True)
Foo False
ghci> newtype Bar = Bar String deriving Show
ghci> under (filter (== 'a')) (Bar "abacaba")
Bar "aaaa"
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到整个模块:

也可以为二元运算符实现自定义函数:

ghci> import Data.Coerce 
ghci> :set -XScopedTypeVariables 
ghci> :set -XTypeApplications 
ghci> :{
ghci| via :: forall n a . Coercible a n => (n -> n -> n) -> (a -> a -> a)
ghci| via = coerce
ghci| :}
ghci> :{
ghci| viaF :: forall n a . Coercible a (n a) => (n a -> n a -> n a) -> (a -> a -> a)
ghci| viaF = coerce
ghci| :}
ghci> via @(Sum Int) @Int (<>) 3 4
7
ghci> viaF @Sum @Int (<>) 3 5
8
Run Code Online (Sandbox Code Playgroud)

  • @IgnatInsarov这是一个常见的误解.`coerce`是完全安全的.例如,你不能"强制"整数到字符串.只有`unsafeCoerce`才有可能. (4认同)

Ben*_*Ben 6

coerce来自Data.Coerce对于这类事情来说非常棒.您可以使用它在具有相同表示形式的不同类型之间进行转换(例如在类型和新类型包装器之间转换,反之亦然).例如:

? coerce (3 :: Int) :: Sum Int
Sum {getSum = 3}
it :: Sum Int

? coerce (3 :: Sum Int) :: Int
3
it :: Int
Run Code Online (Sandbox Code Playgroud)

它的开发来解决,这是经济自由转换,例如一个问题Int变成一个Sum Int应用Sum,但它不一定是成本自由例如转换[Int][Sum Int]应用map Sum.编译器可能能够优化map或不执行列表脊椎的遍历,但我们知道内存中的相同结构可以充当a [Int]或a [Sum Int],因为列表结构不依赖于任何属性.元素和元素类型在这两种情况之间具有相同的表示.coerce(加上角色系统)允许我们利用这个事实在两者之间进行转换,保证不做任何运行时工作,但仍然让编译器检查它是否安全:

? coerce [1, 2, 3 :: Int] :: [Sum Int]
[Sum {getSum = 1},Sum {getSum = 2},Sum {getSum = 3}]
it :: [Sum Int]
Run Code Online (Sandbox Code Playgroud)

对我来说一点都不明显的事情是,coerce不仅限于强迫"结构"!因为它所做的只是允许我们在表示相同时替换类型(包括复合类型的部分),它同样可以强制代码:

? addInt = (+) @ Int
addInt :: Int -> Int -> Int

? let addSum :: Sum Int -> Sum Int -> Sum Int
|     addSum = coerce addInt
| 
addSum :: Sum Int -> Sum Int -> Sum Int

? addSum (Sum 3) (Sum 19)
Sum {getSum = 22}
it :: Sum Int
Run Code Online (Sandbox Code Playgroud)

(在上面的例子中,我必须定义一个monotype版本,+因为它coerce是如此通用的类型系统否则不知道+我要求强制使用哪个版本Sum Int -> Sum Int -> Sum Int;我可以在参数上给出一个内联类型签名coerce,但这看起来不那么整洁.通常在实际使用中,上下文足以确定"源"和"目标"类型的coerce)

我曾经写过一个库,通过newtypes提供了一些不同类型的参数化方法,并为每个方案提供了类似的API.实现API的模块充满了类型签名和foo' = coerce foo样式定义; 除了陈述我想要的类型之外,我几乎没有做任何工作,这感觉非常好.

您的示例(使用mappendon Sum来实现添加,而不必显式地来回转换)可能如下所示:

? let (+) :: Int -> Int -> Int
|     (+) = coerce (mappend @ (Sum Int))
| 
(+) :: Int -> Int -> Int

? 3 + 8
11
it :: Int
Run Code Online (Sandbox Code Playgroud)