为什么编译器不接受返回Int8为" Real c"?

时间:2017-02-08 13:55:49

标签: haskell numbers

我有这个示例代码:

module Main where

import Data.Int
import Data.Text

data JavaValue = JavaString Text
              | JavaByte Int8
              | JavaShort Int16
              | JavaInt Int32
              | JavaLong Int64
              | JavaFloat Float
              | JavaDouble Double
              | JavaChar Char
              | JavaBool Bool
              | ArrayValue [JavaValue]
              | JavaNull deriving (Eq)

getnumberfromvalue :: Real c => JavaValue -> Maybe c
getnumberfromvalue (JavaByte n) =Just n
getnumberfromvalue (JavaShort n) =Just n
getnumberfromvalue (JavaInt n) =Just n
getnumberfromvalue (JavaLong n) =Just n
getnumberfromvalue (JavaFloat n) =Just n
getnumberfromvalue (JavaDouble n) =Just n
getnumberfromvalue (JavaChar n) =Just $ fromEnum n
getnumberfromvalue _ = Nothing

此代码无法使用此错误进行编译:

  

test.hs:25:34:错误:

• Couldn't match type ‘c’ with ‘Int’
  ‘c’ is a rigid type variable bound by
    the type signature for:
      getnumberfromvalue :: forall c. Real c => JavaValue -> Maybe c
    at test.hs:18:23
  Expected type: Maybe c
    Actual type: Maybe Int
• In the expression: Just $ fromEnum n
  In an equation for ‘getnumberfromvalue’:
      getnumberfromvalue (JavaChar n) = Just $ fromEnum n
• Relevant bindings include
    getnumberfromvalue :: JavaValue -> Maybe c (bound at test.hs:19:1)

我不明白为什么,因为Int(8,16,32,64)Float以及Double都是Real,为什么它说它不匹配{{} 1}}与Int?!!

上面的代码只是我为大学项目构建的java编译器的一小部分,c只是一个实用函数,getnumberfromvalue代表一个java文字。

我知道JavaValuefromIntegral,我不认为我可以在这里使用它们。

我看过this问题,但答案是使用GADT,ExistentialQuantification似乎只适用于类型,我不认为我想用它来解决这个简单的问题,即使没有GADT也没有#n;'有没有一种方法不涉及定义一个简单的数据类型?

2 个答案:

答案 0 :(得分:9)

您要表达的是存在类型

{-# LANGUAGE GADTs #-}

data SomeReal where
  SomeReal :: Real c => c -> SomeReal

getnumberfromvalue :: JavaValue -> Maybe SomeReal
getnumberfromvalue (JavaByte n) = Just $ SomeReal n
getnumberfromvalue (JavaShort n) = Just $ SomeReal n
getnumberfromvalue (JavaInt n) = Just $ SomeReal n
...

这基本上等同于OO语言对Real getNumberFromValue (JavaValue v) {...}这样的签名的意义:函数返回一些某种类型,调用者不知道它是什么类型的将(只是它在Real类中)。

但你为什么要这样呢?您可以使用多种未知类型做多少,您甚至无法添加它或与其他数字进行比较,因为类型可能不同。由于Real具体而言, 所做的唯一事情是数字转换为具有类型Rational的数字(被认为是“所有真正的数字类型的超类型”)。好的,但是你可以在前面这样做,不需要在存在主义中包装:

getnumberfromvalue :: JavaValue -> Maybe Rational
getnumberfromvalue (JavaByte n) = Just $ toRational n
getnumberfromvalue (JavaShort n) = Just $ toRational n
getnumberfromvalue (JavaInt n) = Just $ toRational n
...

Real c => JavaValue -> Maybe c这样的标准多态Haskell签名的含义是非常不同的,并且比存在类型更有用:它意味着调用者可以选择要使用的数字类型。这可以是在许多功能中共享的固定类型,在保留动态语言的灵活性的同时,不需要在两者之间进行转换。它与C ++模板更相似,而不是通用的协变OO多态性 但是当然这意味着你必须在开始任何计算之前解决某个特定的类型,你不能只传递未知类型的数字(这会产生大量的运行时开销,基本上是Ruby和Python速度慢的原因,而Java有非int的非对象类型。

IMO这很愚蠢,因为real numbers通常不是理性。 Real a的方法可以说是toCauchySeq :: a -> [(Rational, Rational)]。 (我估计你不会那么喜欢,但是......)

答案 1 :(得分:2)

你可以这样做:

getnumberfromvalue :: Fractional f => JavaValue -> Maybe f
getnumberfromvalue (JavaByte n) = Just . realToFrac $ n
getnumberfromvalue (JavaShort n) = Just . realToFrac $ n
getnumberfromvalue (JavaInt n) = Just . realToFrac $ n
getnumberfromvalue (JavaLong n) = Just . realToFrac $ n
getnumberfromvalue (JavaFloat n) = Just . realToFrac $ n
getnumberfromvalue (JavaDouble n) = Just . realToFrac $ n
getnumberfromvalue (JavaChar n) = Just . realToFrac . fromEnum $ n
getnumberfromvalue _ = Nothing

或者像这样:

getnumberfromvalue :: (Num a, Typeable a) => JavaValue -> Maybe a
getnumberfromvalue (JavaByte n) = Just . fromIntegral $ n
getnumberfromvalue (JavaShort n) = Just . fromIntegral $ n
getnumberfromvalue (JavaInt n) = Just . fromIntegral $ n
getnumberfromvalue (JavaLong n) = Just . fromIntegral $ n
getnumberfromvalue (JavaFloat n) = cast n <|> cast (realToFrac n :: Double)
getnumberfromvalue (JavaDouble n) = cast n <|> cast (realToFrac n :: Float)
getnumberfromvalue (JavaChar n) = Just . fromIntegral . fromEnum $ n
getnumberfromvalue _ = Nothing