在Haskell中重新排序类型参数

3
我是一位有帮助的助手,以下是您需要翻译的内容:

我有一个关于类型参数的问题,我认为最好通过一个例子来表达。以下是这段代码:

newtype Triple a b c = T (a,b,c)

instance Functor (Triple a b) where
    fmap f (T (x, y, z)) = T (x, y, (f z))

将三元组表达为以其第三个变量为函子。

  1. 我应该如何在它们的第二个变量中将它们转化为函子?
  2. 我应该如何将实际元组(而不是我的新类型)转化为函子?

一般问题是:假设我有一个参数类型 m a b c d e,我要如何表示通过固定一个参数所得到的参数类型 m a b d e?或等价地,我要如何表示通过将任意一个参数作为最后一个参数所得到的参数类型 m a b d e c

编辑:可能并不十分清楚我的意思,因此我正在尝试澄清:Triple 的种类为 * -> * -> * -> *。因此,我可以部分评估两个类型,以获得种类为 * -> * 的某些内容,这可以是 Functor 或其他参数化类。这种评估很容易在前两个参数处进行,但原则上在任意两个参数处进行也是可能的,我正在询问如何实现。这本质上就是在类型层面上寻求一个 flip

作为一个具体的用例,我可以有三个参数化类 Functor、Foo 和 Bar,我希望 (Triple _ b c) 是一个 Functor,(Triple a _ c) 是一个 Foo,而 (Triple a b _) 是一个 Bar(对于所有的 a、b 和 c)。因此,Triple a b c 将是一个 Functor、一个 Foo 和一个 Bar。你会考虑编写这些一参数类型 a -> Triple a b cb -> Triple a b cc -> Triple a b c,但当然这个文字表示法表达了映射类型。

编辑2:在发布问题之前,我总是尝试将其剥离到抽象的核心,但这似乎掩盖了我实际想要的内容。因此,这个问题的一个具体变体现在 这里


你可能认为使用TypeSynonymInstances GHC扩展,你可以这样做:type TripleSynonym a c b = (a, b, c),然后instance Functor (TripleSynonym a c)。但是不行:在这种情况下,GHC不允许你部分地评估类型。 - Stefan Witzel
3个回答

3
这就是新类型的作用。你可以在新类型中包装现有类型,从而可以在类型层面上进行不同的操作,同时保持值级别不变。例如:
newtype SecondTriple a b c = SecondTriple (a, c, b)

instance Functor (SecondTriple a b) where
  fmap f (SecondTriple (x, z, y)) = SecondTriple (x, f z, y)

如果你愿意,你可以将Triple用包裹起来,而不是用(,,)来包裹它,但当然你无法使用Triple的Functor实例,因此这并没有太大帮助。


1
我对这个建议有两个问题:1. 如果我为具有n个参数的类型的所有排列引入一个新类型,那么将会给我带来很多新类型(n!);2. 我实际上可能希望元组在第一个参数方面是一个函子,在第二个参数方面属于其他某个类。 - Stefan Witzel

3
在这种情况下,您可以使用镜头来获得所需内容。 over元组模块中的所有函数(_1、_2、_3 等)的组合 的结合使您能够将函数提升到不止右侧位置的元组中。
编辑:添加示例。
因此,假设我们有这个元组。
(1, "Foo", True)

我们希望将第一个位置的值加1。

> import Control.Lens (over, _1)
> over _1 (+ 1) (1, "Foo", True)
(2,"Foo",True)

或者将字符串中第二个位置的字符转换为大写

> import Data.Char (toUpper)
> import Control.Lens (over, _2)
> over _2 (map toUpper) (1, "Foo", True)
(1,"FOO",True)

或者,我们想要翻转其第三个位置的布尔值

> import Control.Lens (over, _3)
> over _3 not (1, "Foo", True)
(1,"Foo",False)

你能举个例子吗?这似乎是足够抽象的层次,但我不熟悉镜头。 - Stefan Witzel
当然,已添加到答案中。 - parkerbrads
谢谢!但是在类型层面上,这个怎么工作呢?本质上,我想改变三元组的类型。 - Stefan Witzel
嗯,我给你的内容是如何将函数提升到不同的元组位置。在我看来,这可能很有用,因为它在任何您喜欢的位置上都可以将元组变成Functors。我认为我需要更多了解你所尝试的内容,才能提供更多帮助。您是否必须使用Functor? - parkerbrads
1
啊,我也意识到在我所有的例子中,你看不到可以改变位置类型的可能性。也就是说:over _1 @(,,) 应该有类型 (a -> d) -> (a, b, c) -> (d, b, c) - parkerbrads
我觉得这可能有帮助,但我看不到它。 - Stefan Witzel

1
一个函数子的类型是 Type -> Type,因此 Triple :: Type -> Type -> Type -> Type 本身不是一个函数子;只有在2种类型 ab 的几乎饱和偏应用 Triple a b 才能成为一个函数子。 Triple 是一个“三元函数子”的例子,你可以自己定义。
class Trifunctor p where
    trimap :: (a -> x) -> (b -> y) -> (c -> z) -> p a b c -> p x y z
    -- There are only so many synonyms for first, second, etc
    map13 :: (a -> x) -> p a b c -> p x y z
    map13 f = trimap f id id
    map23 :: (b -> y) -> p a b c -> p x y z
    map23 f = trimap id f id
    map33 :: (c -> z) -> p a b c -> p x y z
    map33 f = trimap id id f

instance Trifunctor Triple where
    trimap f g h (Triple x y z) = Triple (f x) (g y) (h z)

模式是通用的;n个类型的乘积是一个n-functor。

谢谢,我表达不够准确。我想说的是,“Triple a _ c”是一个函子,“Triple a b _”是一个foo,“Triple _ b c”是一个bar。 - Stefan Witzel
你做不到。所有类型类都有种类为 * -> Constraint,其中 * 是某个特定的种类。类型构造函数像函数一样是柯里化的,因此 Triple a _ c 不是产生种类的有效部分应用程序。 - chepner
更具体地说,你可以说,例如Triple在其第二个参数中是函子,“foo-like”在其第三个参数中,“bar-like”在其第一个参数中。(举个具体的例子,bifunctor在其两个参数中都是函子,而profunctor在其第一个参数中是反函子,在其第二个参数中是函子。)你所需的是某种类型类组合的概念,就我所知,Haskell没有类似的东西。(我也不知道缺少的原因是语言的限制还是缺乏实现它的库.) - chepner
(或者,Triple可以是某种类型类的实例,该类型类描述其实例的条形特性、函子性和foo-likeness。) - chepner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接