2017-02-08 4 views
0

У меня есть этот пример кода:Почему компилятор не принимает возвращаемый Int8 как «Real c»?

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: error:

• 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 почему он говорит, что он не может соответствовать Int с c? !!

Вышеупомянутый код - это лишь небольшая часть компилятора java, который я создаю для университетского проекта, getnumberfromvalue - это просто функция полезности, JavaValue - представлять собой буквальный Java.

Я знаю о fromIntegral и realToFrac, и я не думаю, что могу использовать их здесь.

Я видел this вопрос, но ответ использует GADT и ExistentialQuantification, похоже, только для типов, я не думаю, что хочу использовать его для этой простой проблемы, даже без GADT нет способа, t включает определение тривиального типа данных? ,

+4

Это не так, как полиморфизм работает Haskell. Классы типов не являются подтипированием. Переменные типа выбираются * вызывающим * функцией, а не самой функцией. Есть ли какой-то вопрос, который охватывает это действительно хорошо? – Carl

+0

Редактировать: совершенно неверное утверждение, сохраненное для потомков, следует - О, и, для чего он стоит, типы 'Int' и' Word' не являются экземплярами 'Real'. – Carl

+1

@ Карл Да, они есть. – jpath

ответ

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 
... 

Какого стандарт полиморфный Haskell подпись как Real c => JavaValue -> Maybe c средств довольно сильно отличается и гораздо более полезным, чем экзистенциальные типами: это означает, что вызывающий абонент выбирает тип номера для использования. Это может быть фиксированный тип, который используется совместно во многих функциях, устраняя необходимость в конверсиях между ними, сохраняя при этом гибкость динамического языка. Это больше похоже на шаблоны C++, чем на общий ковариантный полиморфизм OO.
Но, конечно, это означает, что вам придется поселиться на определенном типе, прежде чем начинать какие-либо вычисления, вы не можете просто пройти номера неизвестного типа (что может привести к чрезмерной нагрузке на время выполнения, причина, по которой Ruby и Python медленны, а Java имеет не-объекты типа int).


ИМО это глупо, так как real numbers наказывается вообще не рациональны.Метод Real a должен быть, возможно, toCauchySeq :: a -> [(Rational, Rational)]. (Я думаю, вы не предпочли бы, что, хотя ...)

+1

Почему оскорбляют экзистенциальные типы? Они могут быть очень полезными! В основе пакета 'foldl' лежит тот, который тесно связан с пакетом' Coyoneda', и они также важны для последовательностей с выравниванием по типу (для «отражения без угрызений совести») и т. Д. Конечно, им не место в этой конкретной функции, но можно представить себе некоторую схожую ситуацию, когда они будут иметь смысл, включая множественные связанные числа и функции, завернутые в экзистенциальный. – dfeuer

+0

Я оскорбил экзистенциальные? Я просто сказал, что параметрический полиморфизм более полезен, чем они есть. Да, экзистенции также полезны для специальных приложений _some_. – leftaroundabout

+0

Возможно, я неправильно понял ваш тон. Сожалею. – dfeuer

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 
Смежные вопросы