这个功能可以用 Haskell 的类型系统来实现吗?

发布于 2024-12-08 16:25:51 字数 1005 浏览 1 评论 0原文

在 Scala 中,集合上的高阶操作总是返回上下文中可能的最佳类型。例如,在 BitSet 的情况下,如果将整数映射到整数,您将得到一个 BitSet,但如果您将整数映射到字符串,您将得到一个通用的 Set< /代码>。同样,如果您使用生成一对的函数来映射一个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 翻译,我们将不胜感激。 :-)

In Scala, the higher order operations on collections always return the best possible type in the context. For example, in case of BitSet, if you map ints to ints you get a BitSet, but if you map ints to strings, you get a general Set. Similarly, if you map a Map with a function that yields a pair, then you get a Map in return. Else you get a simple Iterable. Both the static type and the runtime representation of map's result depend on the result type of the function that's passed to it.

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)

Is it possible to implement the same functionality in Haskell's type system? If yes, how? A Haskell translation of the examples in above code snippet would be much appreciated. :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你怎么敢 2024-12-15 16:25:51

我不认为你的第一个例子很 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 更相关,因为它更多的是根据所包含的类型选择最有效的数据结构实现,并且我怀疑使用 类型族,但我对这些不太熟悉,所以我会让其他人尝试回答这部分。

I don't think your first example is very Haskell-y, as you're overloading the same name to do two very different things. In Haskell, when you map a function over some container, you expect to get the same container type back. In fact, this is so common in Haskell that there is a type class, Functor which encapsulates this pattern.

All forms of overloading in Haskell boil down to using type classes, and while you could use those to achieve something similar, it would be very contrived and not very useful over just using plain functions that do just the one thing you want.

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]

I think your second example is more relevant to Haskell, as it's more about picking the most efficient implementation of a data structure based on the contained type, and I suspect this shouldn't be too difficult to do using type families, but I'm not too familiar with those, so I'll let someone else try to answer that part.

丑疤怪 2024-12-15 16:25:51

我非常同意哈马尔的观点,但这里有一种方法可以做到这一点,有点:

{-# 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

但是该实例与成对的实例冲突。因此没有好方法为所有其他类型提供实例。

I very much agree with hammar, but here's a way to do it, sort of:

{-# 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

Here it is in action:

*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]

Ideally you'd want to do this:

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

But that instance conflicts with the one for pairs. So there's no good way to give an instance for every other type.

甜点 2024-12-15 16:25:51

添加到哈马尔:我认为第二个示例就目前情况而言是不可能的,因为存在隐式类型转换。

为了讨论的目的,忽略这一点,类型签名会是什么样子:

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

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

Adding to hammar: I think the second example is not possible as it stands, because there are implicit type conversions.

Ignoring that for the sake of the discussion, how could the type signature look like:

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

So, yes, this is conceivable, yet with the provision that one may need to specify the return type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文