haskell中的多态性 - 使用一个函数的多个版本而不给它不同的名称

eps*_*lbe 5 polymorphism haskell dry

前几天我写了一个小程序来收集矩阵中的一堆数字 - data Matrix = Matrix [[Int]]从一个角落开始 - Corner并按照一个路径 - [Direction]所有这三种类型都是一个类的实例Transformable

然后我有一个函数,生成一个转换函数,转换给定的角和方向 - LeftUpRight

turnLUR :: Transformable a => (Corner, Direction) -> a -> a
Run Code Online (Sandbox Code Playgroud)

然后我用这个函数"收获"矩阵中的所有数字:

harvest :: Matrix ? [(Corner,Direction)] ? [Int]
harvest _ [] = []
harvest (Matrix []) _ = []
harvest as (cd:csds) = b ++ harvest (Matrix bs) csds'  --cd = (Corner,Direction)
                     where f1 = turnLUR cd -- Matrix -> Matrix
                           f2 = turnLUR cd -- Corner -> Corner
                           f3 = turnLUR cd -- Direction -> Direction
                           Matrix (b:bs) = f1 as -- b = first line of [[Int]]
                           fcfd (c,d) = (f2 c,f3 d)
                           csds' = map fcfd csds
Run Code Online (Sandbox Code Playgroud)

现在我的问题为什么我必须写下来f1,f2f3不是使用一个函数f三次(在我的脑海中保持DRY!) - 所有三种类型Corners,Directions并且Matrix是实例class Transformable.

如何在没有制作"相同"功能的三个版本的情况下编写该代码?

ehi*_*ird 11

这是因为单态限制.给它一个显式的类型签名,你每次都可以重用相同的函数:

harvest :: Matrix ? [(Corner,Direction)] ? [Int]
harvest _ [] = []
harvest (Matrix []) _ = []
harvest as (cd:csds) = b ++ harvest (Matrix bs) csds'  --cd = (Corner,Direction)
                     where f :: Transformable a => a -> a
                           f = turnLUR cd
                           Matrix (b:bs) = f as -- b = first line of [[Int]]
                           fcfd (c,d) = (f c,f d)
                           csds' = map fcfd csds
Run Code Online (Sandbox Code Playgroud)

或者,您可以通过放在{-# LANGUAGE NoMonomorphismRestriction #-}文件顶部将其关闭.

单态限制并不受欢迎 - 甚至GHC的手册也将其称为可怕的单态限制 - 但是有一些棘手的极端情况,特别是与共享相关(参见我链接的维基页面以获取更多信息),它避免了.在这种情况下,我建议只添加类型签名.

  • 请注意,单态限制仅影响以无意义样式(以及不是函数的值)编写的函数,因此如果使用显式参数定义`f`,即使不使用显式参数也不会受到限制的影响给它一个类型签名. (3认同)