在 Haskell 中实现临时多态性的最佳方法是什么? [英] Best way to implement ad-hoc polymorphism in Haskell?

查看:28
本文介绍了在 Haskell 中实现临时多态性的最佳方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个多态函数,例如:

I have a polymorphic function like:

convert :: (Show a) => a -> String
convert = " [label=" ++ (show a) ++ "]"

但有时我想给它传递一个 Data.Map 并做一些更花哨的键值转换.我知道我不能在这里进行模式匹配,因为 Data.Map 是一种抽象数据类型(根据 this similar SO question),但我没有成功为此使用警卫,而且我不确定 ViewPatterns 是否会在这里有所帮助(并且宁愿避免使用它们以实现可移植性).

But sometimes I want to pass it a Data.Map and do some more fancy key value conversion. I know I can't pattern match here because Data.Map is an abstract data type (according to this similar SO question), but I have been unsuccessful using guards to this end, and I'm not sure if ViewPatterns would help here (and would rather avoid them for portability).

这正是我想要的:

import qualified Data.Map as M

convert :: (Show a) => a -> String
convert a 
    | M.size =0 = processMap2FancyKVString a -- Heres a Data.Map
    | otherwise = " [label=" ++ (show a) ++ "]" -- Probably a string

但这不起作用,因为 M.size 除了 Data.Map 不能接受任何东西.

But this doesn't work because M.size can't take anything other than a Data.Map.

具体来说,我正在尝试修改 函数图形库中的 sl 实用函数,用于处理 GraphViz 输出中边的着色和其他属性.

Specifically, I am trying to modify the sl utility function in the Functional Graph Library in order to handle coloring and other attributes of edges in GraphViz output.

更新

我希望我能接受 TomMD、Antal S-Z 和 luqui 对这个问题的所有三个答案,因为他们都明白我真正在问什么.我会说:

I wish I could accept all three answers by TomMD, Antal S-Z, and luqui to this question as they all understood what I really was asking. I would say:

  • Antal S-Z 提供了应用于 FGL 的最优雅"的解决方案,但也需要最多的重写和重新思考才能在个人问题中实施.
  • TomMD 给出了一个很好的答案,在适用性与正确性方面介于 Antal S-Z 和 luqui 之间.这也是直接的,我非常感激,为什么我选择了他的答案.
  • luqui 给出了最好的让它快速工作"的答案,我可能会在实践中使用它(因为我是一名研究生,这只是一些用于测试一些想法的一次性代码).我不接受的原因是因为 TomMD 的回答可能会在更一般的情况下更好地帮助其他人.

话虽如此,它们都是很好的答案,上述分类是一种粗略的简化.我还更新了问题标题以更好地代表我的问题(再次感谢大家拓宽了我的视野!

With that said, they are all excellent answers and the above classification is a gross simplification. I've also updated the question title to better represent my question (Thanks Thanks again for broadening my horizons everyone!

推荐答案

你刚才解释的是你想要一个根据输入类型表现不同的函数.虽然您可以使用 data 包装器,从而始终关闭该函数:

What you just explained is you want a function that behaves differently based on the type of the input. While you could use a data wrapper, thus closing the function for all time:

data Convertable k a = ConvMap (Map k a) | ConvOther a
convert (ConvMap m) = ...
convert (ConvOther o) = ...

更好的方法是使用类型类,从而使 convert 函数保持开放和可扩展,同时防止用户输入无意义的组​​合(例如:ConvOther M.empty).

A better way is to use type classes, thus leaving the convert function open and extensible while preventing users from inputting non-sensical combinations (ex: ConvOther M.empty).

class (Show a) => Convertable a where
    convert :: a -> String

instance Convertable (M.Map k a) where
    convert m = processMap2FancyKVString m

newtype ConvWrapper a = CW a
instance Convertable (ConvWrapper a) where
    convert (CW a) = " [label=" ++ (show a) ++ "]"

通过这种方式,您可以将所需的实例用于每种不同的数据类型,并且每次需要新的特化时,您都可以通过添加另一个 instance Convertable 来扩展 convert 的定义NewDataType 其中 ....

In this manner you can have the instances you want used for each different data type and every time a new specialization is needed you can extend the definition of convert simply by adding another instance Convertable NewDataType where ....

有些人可能会对 newtype 包装器皱眉,并建议使用如下实例:

Some people might frown at the newtype wrapper and suggest an instance like:

instance Convertable a where
    convert ...

但这将需要强烈建议不要使用重叠和不可判定的实例扩展,从而为程序员带来极少的便利.

But this will require the strongly discouraged overlapping and undecidable instances extensions for very little programmer convenience.

这篇关于在 Haskell 中实现临时多态性的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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