自然语言类型的子类型

时间:2012-02-09 01:15:30

标签: haskell

我是一名致力于自然语言的正式语法/语义的语言学家。我已经开始了 最近使用Haskell很快就意识到我需要添加子类型。例如给出人类的类型 和动物,  我想将Human作为Animal的子类型。我发现这可以使用强制函数,其中实例由用户声明,但我不知道如何在我感兴趣的实例中定义强制。所以基本上我不知道在'coerce =之后要添加什么'让它发挥作用'。这是到目前为止的代码:

{-# OPTIONS

 -XMultiParamTypeClasses
 -XFlexibleInstances
-XFunctionalDependencies
-XRankNTypes
-XTypeSynonymInstances 
-XTypeOperators
#-}

module Model where 

import Data.List



data Animal = A|B deriving (Eq,Show,Bounded,Enum)

data Man = C|D|E|K deriving (Eq,Show,Bounded,Enum)

class Subtype a b where
coerce :: a->b

instance Subtype Man Animal where
coerce=




animal:: [Animal]
animal = [minBound..maxBound] 

man:: [Man]
man = [minBound..maxBound]

提前致谢

6 个答案:

答案 0 :(得分:12)

只需忽略Subtype类一秒钟,然后检查您正在编写的强制函数的类型。如果aManbAnimal,那么您正在编写的强制函数的类型应为:

coerce :: Man -> Animal

这意味着您所要做的就是编写一个合理的函数,将每个Man构造函数(即C | D | E | K)转换为相应的Animal构造函数(即{{1 }})。这对于子类型意味着什么,您可以在其中定义一些映射" sub"输入原始类型。

当然,您可以想象,因为您的A | B类型有四个构造函数,而Man类型只有两个构造函数,那么您最终会得到多个Animal构造函数映射到相同的Man构造函数。这没有什么不妥,只是意味着强制功能是不可逆的。如果不确切知道那些构造函数应该代表什么,我就无法对此发表更多评论。

您的问题的更一般的答案是,无法自动知道Animal中的哪些构造函数应该映射到Man中的哪些构造函数。这就是为什么你必须写出强制函数来告诉它人与动物之间的关系是什么。

另请注意,'子类型'没有什么特别之处。上课和强迫'功能。你可以跳过它们并写一个“manToAnimal”'功能。毕竟没有内置语言或编译器支持子类型,而Subtype只是一个随机人提出的另一个类(坦率地说,子类型不是真正惯用的Haskell,但你并没有真正询问那)。定义类实例的所有操作都允许您重载函数Animal以处理coerce类型。

我希望有所帮助。

答案 1 :(得分:8)

你在“需要添加子类型”的地方工作了什么级别的抽象?

  1. 您是否正在尝试为Haskell类型编码的程序创建世界模型? (如果您的类型实际 AnimalDog等,我可以看到这一点。)
  2. 您是否正在尝试创建更多通用软件,并且您认为子类型是一个很好的设计?
  3. 或者你只是在学习哈斯克尔并玩弄东西。
  4. 如果(1),我认为这对你来说不会很好。 Haskell没有很好的反射能力 - 即将类型逻辑编织到运行时逻辑中的能力。你的模型最终会与实现完全纠缠在一起。我建议创建一个“世界模型”(一组)类型,而不是一组与特定世界模型相对应的类型。即,为Haskell回答这个问题:什么是世界模型?

    如果(2),再想一想:-)。子类型是Haskell不参与的设计传统的一部分。还有其他方法可以设计你的程序,它们最终会在功能性思维模式中发挥更好的作用,然后进行子类型化。开发您的功能设计意识需要时间,因此请耐心等待。记住:保持简单,愚蠢。使用数据类型和函数(但请记住使用高阶函数来概括和共享代码)。如果你正在寻求高级功能(甚至类型词在我的意思上是相当先进的),你可能做错了。

    如果(3),请看Doug的答案,然后玩一些东西。有很多方法可以伪造它,最终它们都很糟糕。

答案 2 :(得分:5)

我对自然语言知之甚少,所以我的建议可能会忽略这一点,但这可能就是你要找的。

{-# OPTIONS
  -XMultiParamTypeClasses
  -XFlexibleContexts
#-}
module Main where

data Animal = Mammal | Reptile deriving (Eq, Show)
data Dog = Terrier | Hound deriving (Eq, Show)
data Snake = Cobra | Rattle deriving (Eq, Show)

class Subtype a b where
  coerce :: a -> b

instance Subtype Animal Animal where
  coerce = id

instance Subtype Dog Animal where
  coerce _ = Mammal

instance Subtype Snake Animal where
  coerce _ = Reptile

isWarmBlooded :: (Subtype a Animal) => a -> Bool
isWarmBlooded = (Mammal == ) . coerce

main = do
  print $ isWarmBlooded Hound
  print $ isWarmBlooded Cobra
  print $ isWarmBlooded Mammal

给你:

True
False
True

这是你拍摄的那种吗? Haskell没有内置的子类型,但这可能是一种解决方法。无可否认,可能有更好的方法来做到这一点。

注意:此答案并非旨在指出解决手头问题的最佳,正确或自觉方式。它旨在回答这个问题,即“在'coerce ='之后添加什么才能让它发挥作用。”

答案 3 :(得分:4)

你不能写出你正在寻找的coerce功能 - 至少,不是明智的。 Animal中没有与Man中的值对应的值,因此您无法为coerce编写定义。

由于各种原因,Haskell没有将子类型作为显式设计决策(它允许类型推断更好地工作,并且允许子类型使语言的类型系统变得非常复杂)。相反,您应该使用聚合来表达这样的关系:

data Animal = A | B | AnimalMan Man deriving (Eq, Show, Bounded, Enum)
data Man = C | D | E | K deriving (Eq, Show, Bounded, Enum)

AnimalMan现在的类型为Man -> Animal,完全符合您的要求coerce

答案 4 :(得分:2)

如果我理解正确,那很有可能。我们将使用类型类和广义代数数据类型来实现此功能。

如果你想做这样的事情(可以喂养动物和人类,但只有人类可以思考):

animals :: [AnyAnimal]
animals = (replicate 5 . AnyAnimal $ SomeAnimal 10) ++ (replicate 5 . AnyAnimal $ SomeHuman 10 10)

humans :: [AnyHuman]
humans = replicate 5 . AnyHuman $ SomeHuman 10 10

animals' :: [AnyAnimal]
animals' = map coerce humans

animals'' :: [AnyAnimal]
animals'' = (map (\(AnyAnimal x) -> AnyAnimal $ feed 50 x) animals) ++
            (map (\(AnyAnimal x) -> AnyAnimal $ feed 50 x) animals') ++
            (map (\(AnyHuman x) -> AnyAnimal $ feed 50 x) humans)

humans' :: [AnyHuman]
humans' = (map (\(AnyHuman x) -> AnyHuman . think 100 $ feed 50 x) humans)

然后就可以了,例如:

{-# LANGUAGE GADTs                  #-}
{-# LANGUAGE MultiParamTypeClasses  #-}

-- | The show is there only to make things easier
class (Show a) => IsAnimal a where
    feed :: Int -> a -> a
    -- other interface defining functions

class (IsAnimal a) => IsHuman a where
    think :: Int -> a -> a
    -- other interface defining functions

class Subtype a b where
    coerce :: a -> b

data AnyAnimal where
    AnyAnimal :: (IsAnimal a) => a -> AnyAnimal
instance Show AnyAnimal where
    show (AnyAnimal x) = "AnyAnimal " ++ show x

data AnyHuman where
    AnyHuman :: (IsHuman a) => a -> AnyHuman
instance Show AnyHuman where
    show (AnyHuman x) = "AnyHuman " ++ show x

data SomeAnimal = SomeAnimal Int deriving Show
instance IsAnimal SomeAnimal where
    feed = flip const

data SomeHuman = SomeHuman Int Int deriving Show
instance IsAnimal SomeHuman where
    feed = flip const
instance IsHuman SomeHuman where
    think = flip const

instance Subtype AnyHuman AnyAnimal where
    coerce (AnyHuman x) = AnyAnimal x

animals :: [AnyAnimal]
animals = (replicate 5 . AnyAnimal $ SomeAnimal 10) ++ (replicate 5 . AnyAnimal $ SomeHuman 10 10)

humans :: [AnyHuman]
humans = replicate 5 . AnyHuman $ SomeHuman 10 10

animals' :: [AnyAnimal]
animals' = map coerce humans

很少有评论:

  • 为方便起见,您可以为其各自的类创建AnyAnimal和AnyHuman实例(atm。您必须先将它们解压缩并在之后打包)。

  • 我们可以像这样使用单个GADT AnyAnimal(我猜这两种方法都有用):

    data AnyAnimal where
        AnyAnimal :: (IsAnimal a) => a -> AnyAnimal
        AnyHuman :: (IsHuman a) => a -> AnyAnimal
    instance Show AnyHuman where
        show (AnyHuman x) = "AnyHuman " ++ show x
        show (AnyAnimal x) = "AnyAnimal " ++ show x
    
    instance Subtype AnyAnimal AnyAnimal where
        coerce (AnyHuman x) = AnyAnimal x
        coerce (AnyAnimal x) = AnyAnimal x
    

答案 5 :(得分:1)

它相当先进,但请看看Edward Kmett的work使用新的Constraint类型来实现这种功能。