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

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

问题描述

我有一个多态函数,如:

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



<但有时候我想通过它传递一个Data.Map并做一些更加奇妙的键值转换。我知道我不能在这里模式匹配,因为Data.Map是一个抽象数据类型(根据这个类似的SO问题),但我一直没有成功使用警卫来达到这个目的,而且我不确定ViewPatterns是否会在这里帮助(而且为了便携性避免使用它们)。



这是我想要的:

 导入限定数据。 Map as M 

convert ::(Show a)=> a - >字符串
转换
| M.size \ = 0 = processMap2FancyKVString a - Heres a Data.Map
|否则=[label =++(show a)++] - 可能是一个字符串



<但是这不起作用,因为M.size除了Data.Map之外不能取任何其他东西。



具体来说,我试图修改 sl功能图库中的效用函数,以便处理GraphViz输出中边缘的着色和其他属性。

更新



我希望我可以接受TomMD,Antal深圳和卢奎这个问题,因为他们都明白我真的在问什么。我会说:


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



有了这些说法,他们都是非常好的答案,上面的分类是一个粗略的简化。我还更新了问题标题以更好地代表我的问题(谢谢再次感谢您扩大我的视野!)

刚刚解释过,你需要一个基于输入类型的行为不同的函数,而你可以使用 data wrapper,从而关闭所有时间的函数:

  data可转换ka = ConvMap(Map ka)| ConvO 
convert(ConvMap m)= ...
convert(ConvOther o)= ...

更好的方法是使用类型类, convert 函数打开并可扩展,同时防止用户输入非感性组合(例如: ConvOther M.empty )。

  class(显示a)=>可转换a其中
转换:: a - >字符串

实例可转换(M.Map ka)其中
转换m = processMap2FancyKVString m

newtype ConvWrapper a = CW
实例Convertable(ConvWrapper a)其中
convert(CW a)=[label =++(show a)++]

通过这种方式,您可以为每种不同的数据类型使用您想要使用的实例,并且每次需要新的特化时,您都可以扩展<$ c通过添加另一个实例Convertable NewDataType其中...



转换 >有些人可能会对 newtype 包装器皱眉,并建议一个实例:

  instance Convertable a where 
convert ...

但是这需要强烈的不鼓励重叠和不可确定的实例扩展,为程序员提供了便利。


I have a polymorphic function like:

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

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).

This is more what I want:

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

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

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.

Update

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 gave the most 'elegant' solution as applied to the FGL but would also require the most rewriting and rethinking to implement in personal problem.
  • TomMD gave a great answer that lies somewhere between Antal S-Z's and luqui's in terms of applicability vs. correctness. It also is direct and to the point which I appreciate greatly and why I chose his answer.
  • luqui gave the best 'get it working quickly' answer which I will probably be using in practice (as I'm a grad student, and this is just some throwaway code to test some ideas). The reason I didn't accept was because TomMD's answer will probably help other people in more general situations better.

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!

解决方案

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) = ...

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) ++ "]"

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 ....

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中实现ad-hoc多态的最佳方式是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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