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

nic*_*man 0 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
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

我不明白为什么,因为Int(8,16,32,64)FloatDouble都是Real为什么它说,它无法比拟Intc?!

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

我知道fromIntegralrealToFrac我不认为我可以在这里使用它们.

我已经看到了这个问题,但答案使用GADT和ExistentialQuantification似乎只适用于类型,我不认为我想用它来解决这个简单的问题,即使没有GADT也没有一种方法不涉及定义一个琐碎的数据类型?.

lef*_*out 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
...
Run Code Online (Sandbox Code Playgroud)

这基本上等同于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
...
Run Code Online (Sandbox Code Playgroud)

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


IMO这是愚蠢的,因为真正的数字是实实在在的,一般理性的.Real a应该可以说是toCauchySeq :: a -> [(Rational, Rational)]相反的方法.(我估计你不会喜欢那个,不过......)