如何判断变量是否是Haskell中的某个数据?

时间:2014-10-31 01:43:53

标签: haskell polymorphism

编辑:QWhere的这个类实例在传递这样的输入时失败:>qWhere fly john即使fly是类型Argument -> Argument -> Predicatejohn是类型Argument

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

data Argument = Argument { ttype :: Type, value :: String } deriving (Show, Eq)
data Predicate = Predicate { lemma :: String, arguments :: [Argument] } deriving (Show, Eq)

class Fly a b where
      fly :: a -> b -> Predicate

instance Fly Argument Argument where
      fly x y = Predicate { lemma = "fly", arguments = [x, y] }

instance Fly Argument Predicate where
      fly x y = Predicate { lemma = "fly", arguments = [x, arguments y !! 0] }

class QWhere a b where
            qWhere :: a -> b -> String

instance QWhere (Argument -> Argument -> Predicate) Argument where
            qWhere x y = "hi"

这是ghci的输出:

No instance for (QWhere (a0 -> b0 -> Predicate) Argument)
  arising from a use of ‘qWhere’
The type variables ‘a0’, ‘b0’ are ambiguous
Note: there is a potential instance available:
  instance QWhere (Argument -> Argument -> Predicate) Argument
    -- Defined at new_context.hs:116:10
In the expression: qWhere fly john
In an equation for ‘it’: it = qWhere fly john

No instance for (Fly a0 b0) arising from a use of ‘fly’
The type variables ‘a0’, ‘b0’ are ambiguous
Note: there are several potential instances:
  instance Fly Argument Predicate
    -- Defined at new_context.hs:110:10
  instance Fly Argument Argument
    -- Defined at new_context.hs:107:10
In the first argument of ‘qWhere’, namely ‘fly’
In the expression: qWhere fly john
In an equation for ‘it’: it = qWhere fly john

这些问题是相关的,但没有一个答案解决了我的问题。

(1)Checking for a particular data constructor

(2)Test if Haskell variable matches user-defined data type option

一些互联网资源应该解决这个问题,但我找不到解决方案:

(3)https://www.haskell.org/haskellwiki/Determining_the_type_of_an_expression

(4)http://okmij.org/ftp/Haskell/typeEQ.html

我的问题:我定义了两个Haskell 数据类型。我得到一个输入,我需要确定它是属于数据类型A还是数据类型B.

以下是数据类型定义:

data Argument = Argument { ttype :: Type, value :: String } deriving (Show, Eq)
data Predicate = Predicate { lemma :: String, arguments :: [Argument] } deriving (Show, Eq)

如果变量是数据类型Argument或Predicate,我需要一个返回true / false的函数。

我试图按照两个SO问题的答案,但只得到ghci编译器的投诉:

--checks if a variable is of data type Argument
--this does not compile (from question (2))
isArgument :: a -> Bool
isArgument (Argument _) = True
isArgument _ = False

--from question (1), also fails
isArgument :: a -> String
isArgument value = 
    case token of
        Argument arg -> "is argument"
        Predicate p -> "is predicate"

3 个答案:

答案 0 :(得分:2)

您尝试执行的动态类型很少在Haskell中使用。如果您想编写可以同时使用PredicateArgument值的函数,则至少有两种惯用方法,具体取决于您的确切用例。

第一种是使用类型类重载函数。 E.g。

class IsArgument a where
    isArgument :: a -> Bool

instance IsArgument Argument where
    isArgument _ = True

instance IsArgument Predicate where
    isArgument _ = False

第二种方法是使用一些总和类型,例如Either Predicate Argument或自定义和类型,例如:

data Expr = ArgumentExpr Argument | PredicateExpr Predicate

isArgument :: Expr -> Bool
isArgument (ArgumentExpr _) = True
isArgument _                = False

您还可以创建相同类型的ArgumentPredicate构造函数,但当然您会失去将它们视为单独类型的类型安全性。您可以通过使用GADT并使用幻像类型标记构造函数来避免这种情况,但这会进入GHC提供的稍高级的类型扩展:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE StandaloneDeriving #-}

data ExprType = Argument | Predicate

data Expr t where
    ArgumentExpr  :: { ttype :: Type, value :: String } -> Expr Argument
    PredicateExpr :: { lemma :: String, arguments :: [Expr Argument] } -> Expr Predicate

deriving instance Show (Expr t)
deriving instance Eq (Expr t)

isArgument :: Expr t -> Bool
isArgument (ArgumentExpr {}) = True
isArgument _ = False

现在,参数和谓词是相同类型的构造函数,但您可以使用类型参数将值限制为特定构造函数,如arguments :: [Expr Argument]中所做的那样,但您也可以使用类型{{ 1}}与Expr t中一样。

如果你真的需要运行时多态性,可以使用Typeable类型类来实现,它使您能够获取运行时类型信息并对不透明的泛型类型进行类型转换。

isArgument

函数cast尝试将任何{-# LANGUAGE DeriveDataTypeable #-} import Data.Typeable data Argument = Argument { ttype :: Type, value :: String } deriving (Show, Eq, Typeable) data Predicate = Predicate { lemma :: String, arguments :: [Argument] } deriving (Show, Eq, Typeable) isArgument :: Typeable a => a -> Bool isArgument a = case cast a of Just (Argument {}) -> True _ -> False 值转换为某种已知类型,如果类型转换成功则返回Typeable a => a值,如果失败则返回Just。 / p>

答案 1 :(得分:1)

你可以通过使Argument和Predicate成为相同类型的一部分来做你想要的......

data LogicElement = Argument { ttype :: Type, value :: String } |
            Predicate { lemma :: String, arguments :: [LogicElement] } deriving (Show, Eq)

虽然可以定义类型(a-> Bool)的函数,但这很不寻常,并且通常意味着输入的值将被忽略(即,如果你不这样做,你怎么做任何事情&# 39;甚至不知道它是什么?你几乎只能在其上应用其他(a-> b)函数。)

在特定示例中,编译器将在以下

中进行投诉
isArgument (Argument _) = True

因为模式隐式暗示输入参数必须是Argument类型,而您提供的签名是未定义类型a

答案 2 :(得分:0)

如果您说isArgument的类型为a -> Bool,那么您说它可以任何,或者更确切地说 {{ 1}},而不仅仅是某些。但是,有一些解决方案。我的偏好是,对于简单的替代方案,只需使用a

Either

或者

import Data.Either

isArgument :: Either Argument Predicate -> Bool
isArgument = isLeft

虽然,这种函数的唯一用途是当你不确定你拥有哪种数据类型时,并且不能模糊地输入Haskell中的值。如果您使用Java / C / C ++ / C#之类的东西,那就相当于

-- Note the use of {} instead of _, the {} expands to all fields in a record
-- the _ only would have taken the place of the ttype field, although you could have
-- (Argument _ _) to capture both fields of the Argument constructor
isArgument :: Either Argument Predicate -> Bool
isArgument (Left (Argument {})) = True
isArgument _ = False

这些是静态类型语言,变量不能采用两种不同类型的值。 Haskell也是如此。如果您想为可以采用两个不同值的变量赋予类型,则必须为其编写容器。 Haskell中的if (some_condition) { x = "a string"; } else { x = 5; } 容器是为此目的预定义的。