可以使用Haskell的类型系统实现此功能吗?

时间:2011-10-09 17:12:21

标签: scala haskell programming-languages functional-programming type-systems

在Scala中,对集合的高阶操作始终在上下文中返回最佳类型。例如,在BitSet的情况下,如果将整数映射到整数,则会得到BitSet,但如果将整数映射到字符串,则会得到一般Set。同样,如果你map Map一个函数产生一对,那么你得到一个Map作为回报。否则你会得到一个简单的Iterable。 map的结果的静态类型和运行时表示都取决于传递给它的函数的结果类型。

scala> Map(2 -> 'a', 6 -> 'b') map { case (k, v) => (k + 1, v.toString) }
res0: scala.collection.immutable.Map[Int,java.lang.String] = Map(3 -> a, 7 -> b)

scala> Map(2 -> 'a', 6 -> 'b') map { _._1 }
res1: scala.collection.immutable.Iterable[Int] = List(2, 6)

scala> import collection.immutable.BitSet
import collection.immutable.BitSet

scala> BitSet(2, 44, 93).map(1 +)
res3: scala.collection.immutable.BitSet = BitSet(3, 45, 94)

scala> BitSet(2, 44, 93).map(_ + "hola")
res4: scala.collection.immutable.Set[String] = Set(2hola, 44hola, 93hola)

是否可以在Haskell的类型系统中实现相同的功能?如果有,怎么样?上面的代码片段中的示例的Haskell转换将非常受欢迎。 : - )

3 个答案:

答案 0 :(得分:11)

我认为你的第一个例子不是Haskell-y,因为你正在重载相同的名字来做两个非常不同的事情。在Haskell中,当您将函数映射到某个容器时,您希望返回相同的容器类型。事实上,这在Haskell中很常见,有一个类型类Functor封装了这种模式。

Haskell中的所有形式的重载都归结为使用类型类,虽然你可以使用它们来实现类似的东西,但它只是使用普通函数来完成你想要的一件事,它会非常有用并且不是很有用。

Prelude> import qualified Data.Map as M
Prelude Data.Map> let m = M.fromList [(2, 'a'), (6, 'b')]
Prelude Data.Map> M.map show $ M.mapKeys (+1) m
fromList [(3,"'a'"),(7,"'b'")]
Prelude Data.Map> M.keys m
[2,6]

我认为你的第二个例子与Haskell更相关,因为它更多的是基于所包含的类型选择最有效的数据结构实现,我怀疑使用{{3}这不应该太难。但是我对这些不太熟悉,所以我会让其他人试着回答那个部分。

答案 1 :(得分:7)

我非常同意哈马尔,但这是一种方法,有点:

{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleInstances #-}

import Prelude hiding (map)

import qualified Data.Map as M
import qualified Data.IntSet as I
import qualified Data.Set as S

type family Elem c :: *
type instance Elem (M.Map k v) = (k, v)
type instance Elem I.IntSet = Int
type instance Elem (S.Set a) = a

class Map c o where
  type Result c o :: *
  map :: (Elem c -> o) -> c -> Result c o

instance Map I.IntSet Int where
  type Result I.IntSet Int = I.IntSet
  map = I.map

instance Map I.IntSet String where
  type Result I.IntSet String = S.Set String
  map f = S.fromList . fmap f . I.toList

instance (Ord k, Ord k1) => Map (M.Map k v) (k1, v1) where
  type Result (M.Map k v) (k1, v1) = M.Map k1 v1
  map f = M.fromList . fmap f . M.toList

instance (Ord k) => Map (M.Map k v) Int where
  type Result (M.Map k v) Int = [Int]
  map f = fmap f . M.toList

这是在行动:

*Main> map fst (M.fromList [(2::Int, 'a'), (6, 'b')])
[2,6]
*Main> map (\(k, v) -> (k + 1, show v)) (M.fromList [(2, 'a'), (6, 'b')])
fromList [(3,"'a'"),(7,"'b'")]
*Main> :t it
it :: M.Map Integer [Char]

理想情况下,你想要这样做:

instance (Ord k) => Map (M.Map k v) a where
  type Result (M.Map k v) a = [a]
  map f = fmap f . M.toList

但是那个实例与成对的实例冲突。所以没有什么好方法可以为其他所有类型提供实例。

答案 2 :(得分:1)

添加到hammar:我认为第二个例子不可能,因为存在隐式类型转换。

为了讨论,忽略这一点,类型签名怎么样:

setmap :: (Set a, Set b) => a e -> (e -> f) -> b f 

所以,是的,这是可以想象的,但是规定可能需要指定返回类型。

相关问题