简体   繁体   English

针对类型变量/ Haskell中某种灵活的函数多态性的值构造函数进行模式匹配

[英]Pattern matching against value constructors in type variables / some sort of flexible function-polymorphism in Haskell

Let's say I have the algebraic data type of a binary tree defined as: 假设我的二叉树的代数数据类型定义为:

data Tree a = Empty | Node a (Tree a) (Tree a)

To build trees, I now want to define a function (-<) such that I can create trees like this: 为了构建树,我现在想定义一个函数(-<)这样我就可以创建树:

1-<(2,3)      1-<(2-<(3,4),5)      1-<(Empty,2-<(4,5))
  1                 1                        1
 / \               / \                        \
2   3             2   5                        2
                 / \                          / \
                3   4                        4   5

It seems it impossible! 看来不可能! My reasoning is that it must have type signature a -> (a, a) -> Tree a) . 我的理由是它必须具有类型签名a -> (a, a) -> Tree a) I then thought that it might be possible to interpret something of type a either as Tree b for some other type b or, if that fails, plainly as a . 那么我认为这也许可以解释型的东西a无论是作为Tree b的一些其他类型的b或者,如果失败,显然是a In the same manner I tried to define 我尝试以相同的方式定义

f :: a -> Int
f (Just _) = 1
f _ = 0

which would be a function that told me whether a value is of type Maybe a and constructed by Just or not. 这将是一个告诉我值是否为Maybe a类型且由Just构造或不构造的函数。 But this doesn't work – ghc then wants the type signature to be Maybe a -> Int . 但这是行不通的-ghc然后希望类型签名为Maybe a -> Int a- Maybe a -> Int

Unless I don't know of any special feature in Haskell, what I want is impossible. 除非我不知道Haskell的任何特殊功能,否则我想要的是不可能的。 My questions now are: 我的问题是:

  • Is it? 是吗? Or do I miss something? 还是我想念什么?
  • What would be a good approximation to what I want? 什么是我想要的一个很好的近似值? (I want to go for succintness, so eg writing (Right 2 -<(Right 3, Left (Right 4 -<(Right 1, Right 5))) is no solution.) (我想简洁,所以例如,写(Right 2 -<(Right 3, Left (Right 4 -<(Right 1, Right 5)))是没有解决的。)

PS I really don't know how to title this question. 附言:我真的不知道该如何称呼这个问题。

You could just create three different operators: 您可以创建三个不同的运算符:

(-<) :: a -> (a, a) -> Tree a
p -< (l, r) = Node p (Node l Empty Empty) (Node r Empty Empty)

(-<\) :: a -> (Tree a, a) -> Tree a
p -<\ (lt, r) = Node p lt (Node r Empty Empty)

(-</) :: a -> (a, Tree a) -> Tree a
p -</ (l, rt) = Node p (Node l Empty Empty) rt

then your trees can be expressed as 那么你的树可以表示为

1-<(2,3)
1-<\(2-<(3,4),5)

and

1-<\(Empty,2-<(4,5))

The function you seek can't have type a -> (a, a) -> Tree a) in Haskell; 寻找的函数在Haskell中不能输入a -> (a, a) -> Tree a) it simply isn't well-typed at all. 它根本没有被很好地键入。 The reason is that those a s could be any type at all, so you can't use their values in any operation that needs a property of any specific type (such as checking whether they are an Empty or Node value). 其原因是,那些a小号可以是任何类型可言,所以你不能在需要的任何特定类型的属性(如检查是否是一个任何操作中使用他们的价值观EmptyNode值)。

The function you're asking for is also ambiguously defined when you remember that you can put trees inside trees, and so a possibility for those a s could actually be Tree b . 当您记得可以将树放入树中时,所要查询的函数也被模棱两可,因此,那些a s的可能实际上是Tree b For example, if I tried Empty -< (Empty, Empty) to make a Tree (Tree t) (for some t ), is this supposed to return Node Empty Empty Empty where I interpret the (Empty, Empty) as a pair of trees, or Node (Node Empty Empty) (Node Empty Empty) where I interpret the (Empty, Empty) as a pair of "bare values" that need to be turned into leaf nodes? 例如,如果我尝试用Empty -< (Empty, Empty)制作一Tree (Tree t) (对于某些t ),这是否应该返回Node Empty Empty Empty ,我将(Empty, Empty)解释为一对树,还是Node (Node Empty Empty) (Node Empty Empty) ,其中我将(Empty, Empty)解释为一对需要变为叶节点的“裸值”? (Or one of the other permutations?) (或其他排列之一?)

You could design a language so that this ambiguity is resolvable, but Haskell avoids the issue by making values in a completely arbitrary type (like a ) completely opaque. 您可以设计一种语言来解决这种歧义,但是Haskell通过使值完全不透明(完全像a任意类型)(例如)避免了这个问题。 You basically can't do anything at all with such a value other than pass it on to another function that accepts a completely arbitrary type; 除了将其传递给接受完全任意类型的另一个函数外,您基本上不能用这种值做任何事情。 this can seem limiting, but this is actually a key part of how Haskell types work and responsible for a lot of the safety guarantees that make a lot of generic library code work. 这似乎是有限的,但这实际上是Haskell类型如何工作的关键部分,并负责使许多通用库代码起作用的许多安全保证。

So what you need is for every position to be expecting an a , or a Tree a ; 因此,您需要的是每个职位都期望aTree a it's not possible to accept an "anything" and see whether it is a Tree a . 不可能接受“任何东西”,看看它是否是Tree a One way to do that is to use a family of functions that covers all the possibilities, as in Lee's answer. 一种解决方法是使用涵盖所有可能性的一系列功能,如Lee的回答。 That's a lot less fun if there are more than two positions! 如果职位多于两个,那就太有趣了! Another possibility is to only accept trees and use a projection function that turns "bare values" into singleton trees 1 . 另一种可能性是接受树并使用将“裸值”转换为单例树1的投影函数。 eg 例如

data Tree a = Empty | Node a (Tree a) (Tree a)
    deriving (Show, Eq, Ord)

(-<) :: a -> (Tree a, Tree a) -> Tree a
x -< (l, r) = Node x l r

t :: a -> Tree a
t x = Node x Empty Empty

Then you are effectively resolving the ambiguity I mentioned above by using t to "tag" everything that needs to be turned into a tree, while everything without the tag must already be a tree of the correct type. 然后,您可以通过使用t对需要转换为树的所有内容进行“标记”来有效地解决我上面提到的歧义,而没有标记的所有内容都必须已经是正确类型的树。

Your examples would then be written as: 您的示例将写为:

*Main> 1 -< (t 2, t 3)
Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty)
*Main> 1 -< (2 -< (t 3, t 4), t 5)
Node 1 (Node 2 (Node 3 Empty Empty) (Node 4 Empty Empty)) (Node 5 Empty Empty)
*Main> 1 -< (Empty, 2 -< (t 4, t 5))
Node 1 Empty (Node 2 (Node 4 Empty Empty) (Node 5 Empty Empty))

1 This is basically the return function you would write if you were making Tree into a monad, but return is a bit long to sprinkle all through your succinct tree expressions, and seems unrelated to tree-making, so I think a better/shorter name is helpful here ( t isn't necessarily great, but it's short and I wasn't feeling terribly inspired). 1这基本上是您将Tree变成monad时要编写的return函数,但是return有点长,无法遍历简洁的树表达式,而且似乎与制树无关,因此我想一个更好/更短的名字在这里有帮助的( t不一定很大,但它的短,我并没有感觉非常的启发)。

You can write a class which does what you want pretty easily: 您可以编写一个可以轻松实现所需功能的类:

{-# LANGUAGE MultiParamTypeClasses, 
  FlexibleInstances, FunctionalDependencies, TypeFamilies, OverlappingInstances, UndecidableInstances #-}

data Tree a = Empty | Node a (Tree a) (Tree a) deriving Show

class Leaf a b | a -> b where
    leaf :: a -> b

instance Leaf (Tree a) (Tree a) where leaf = id

instance (d ~ Tree c) => Leaf c d where leaf a = Node a Empty Empty

mkTree a (b,c) = Node a (leaf b) (leaf c)

The only problem with this approach is that due to defaulting rules, things like mkTree 1 (1,2) will fail because the integer literals are polymorphic, whereas mkTree (1 :: Int) ((1 :: Int),(2 :: Int)) will work. 这种方法的唯一问题是,由于默认规则,诸如mkTree 1 (1,2)类的东西将失败,因为整数文字是多态的,而mkTree (1 :: Int) ((1 :: Int),(2 :: Int))将起作用。 You can make it 'work' with fully polymorphic types by turning on IncoherentInstances but this makes things behave even more strangely so it isn't the best solution. 您可以通过打开IncoherentInstances使它与完全多态类型一起工作,但这会使事情表现得更加奇怪,因此它不是最佳解决方案。

You mentioned you want a succinct syntax and the 2nd option is only slightly less succinct but will always work when it should instead of giving cryptic type errors, including with fully polymorphic types: 您提到要使用简洁的语法,第二个选项的简洁性稍差一些,但始终可以正常工作,而不必给出含糊的类型错误,包括完全多态类型:

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

data Tree a = Empty | Node a (Tree a) (Tree a) deriving Show

data L a = L a 

class Leaf' a b | a -> b where
    leaf' :: a -> b

instance Leaf' (L a) (Tree a) where
    leaf' (L a) = Node a Empty Empty

instance Leaf' (Tree a) (Tree a) where
    leaf' = id

mkTree' a (b,c) = Node a (leaf' b) (leaf' c)

>1-<(L 2,L 3)
Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty)
>1-<(2-<(L 3,L 4),L 5)
Node 1 (Node 2 (Node 3 Empty Empty) (Node 4 Empty Empty)) (Node 5 Empty Empty)
>1-<(Empty,2-<(L 4,L 5))
Node 1 Empty (Node 2 (Node 4 Empty Empty) (Node 5 Empty Empty))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM