Как принять параметры, ограниченные только типом класса - PullRequest
0 голосов
/ 24 марта 2011

Код ниже. Я хотел бы иметь параметры для моей функции, которые ограничены только классом типа. Я вызываю на них функцию класса type и затем могу использовать их. Но я получаю различные ошибки, когда пытаюсь это сделать.

{-# LANGUAGE RankNTypes, TypeSynonymInstances, FlexibleInstances, UndecidableInstances #-}

class PlotValue a where
  value :: a -> Double
instance PlotValue Double where
  value = id
--instance PlotValue Int where
  --value x = fromIntegral x
instance (Integral a) => PlotValue a where
  value x = fromIntegral x
instance PlotValue String where
  value x = 5

type Input = (PlotValue a, PlotValue b) => (Maybe a, Maybe b)

test :: Input -> String
test (Just a, Just b) = (show $ value a) ++ (show $ value b)

main = do
  putStrLn (show ( test (Just "strl", Just 6.4)))

Текущие ошибки (хотя они немного меняются в зависимости от того, что я пробую):

Test5.hs:17:5:
    Couldn't match expected type `Input' against inferred type `(a, b)'
    In the pattern: (Just a, Just b)
    In the definition of `test':
        test (Just a, Just b) = (show $ value a) ++ (show $ value b)

Test5.hs:20:30:
    Couldn't match expected type `a' against inferred type `[Char]'
      `a' is a rigid type variable bound by
          the polymorphic type
            `forall a b. (PlotValue a, PlotValue b) => (Maybe a, Maybe b)'
            at Test5.hs:20:19
    In the first argument of `Just', namely `"strl"'
    In the expression: Just "strl"
    In the first argument of `test', namely `(Just "strl", Just 6.4)'

Test5.hs:20:43:
    Could not deduce (Fractional b)
      from the context (PlotValue a, PlotValue b)
      arising from the literal `6.4' at Test5.hs:20:43-45
    Possible fix:
      add (Fractional b) to the context of
        the polymorphic type
          `forall a b. (PlotValue a, PlotValue b) => (Maybe a, Maybe b)'
    In the first argument of `Just', namely `6.4'
    In the expression: Just 6.4
    In the first argument of `test', namely `(Just "strl", Just 6.4)'

Ответы [ 3 ]

2 голосов
/ 24 марта 2011

Вам действительно нужно изобретать здесь новые классы типов? Prelude механизм достаточно сложен, вы думаете. Это может быть связано только с включением String, но есть и другие способы. Кажется, вы просто хотите общее отображение из стандартных числовых типов (Int, Integer, Float, Double) в Double. Есть много способов сделать это, но что насчет d здесь, вместо вашего value?

d :: Real a => a -> Double
d  = fromRational . toRational 

test (Just a, Just b) = show (d a) ++ "  " ++ show (d b)
test (_, _)= "Something's missing"

-- Main> :t test
-- test :: (Real a, Real a1) => (Maybe a, Maybe a1) -> [Char]

double :: Double
double = 1.0
float :: Float
float = 1.0
int :: Int
int = 1
integer :: Integer
integer = 2

omnibus = d double * d float * d int / d integer

jdouble = Just double
jinteger = Just integer

goodtest = (jdouble,jinteger)
badtest =  (Nothing, jinteger)

main = print omnibus >> putStrLn (test goodtest) >> putStrLn (test badtest)

-- Main> main
-- 0.5
-- 1.0  2.0
-- Something's missing

Если вы хотите применить d к String, то вы хотите обрабатывать строки числами. Хорошо, один из способов сделать это - определить экземпляр Num для String с целью создания экземпляра Real. Просто Google "экземпляр Num String", или посмотрите, например, это замечание dons для примеров. Вот несерьезный пример:

instance Num String where
  fromInteger  = show
  (+) =  (++)
  x * y = concatMap (const y) x
  abs = undefined
  signum = undefined

instance Real String where toRational = toRational . d . length
-- Main> fromInteger 500 * "moo "
-- "moo moo moo "
-- Main> d (fromInteger 500 * "moo")
-- 12.0

stringy = d "string"
jstringy = Just stringy 
stringytest = (jstringy, jinteger)

main' = print omnibus >> print stringy >>  
        putStrLn (test goodtest) >> putStrLn (test badtest) >> 
        putStrLn (test stringytest)
-- Main> main'
-- 0.5
-- 5.0
-- 1.0  2.0
-- Something's missing
-- 5.0  2.0

Или, если вам нужен класс типа PlotValue с value, почему бы не создать его отдельно для четырех ведущих числовых типов и String? На самом деле, тип Input, который вам нужен, действительно похож на (Maybe Double, Maybe Double).

Обратите внимание, что там, где вы пишете

main = do
  putStrLn (show ( test (Just "strl", Just 6.4)))

вам не нужно do, так как вы видите только одно действие; и вам не нужно показывать, так как test уже дает String.

2 голосов
/ 24 марта 2011

Исправлено несколько мелких вещей. В основном, как указал Стивен, скрывать переменную свободного типа под синонимом типа обычно глупо и плохо.

{-# LANGUAGE RankNTypes, TypeSynonymInstances, FlexibleInstances, OverlappingInstances, UndecidableInstances #-}

class PlotValue a where
  value :: a -> Double
instance PlotValue Double where
  value = id
instance (Integral a) => PlotValue a where
  value x = fromIntegral x
instance PlotValue String where
  value x = 5

test :: (PlotValue a, PlotValue b) => (Maybe a, Maybe b) -> String
test (Just a, Just b) = (show $ value a) ++ (show $ value b)

main = do
  putStrLn (show ( test (Just "strl", Just (6.4::Double))))
1 голос
/ 24 марта 2011

Чтобы обойти (на мой взгляд) синоним "странного" типа, я бы выбрал GADT, например:

{-# LANGUAGE GADTs #-} -- in addition to the rest
data Input where
    Input :: (PlotValue a, PlotValue b) => Maybe a -> Maybe b -> Input
test :: Input -> String
test (Input (Just a) (Just b)) = (show $ value a) ++ (show $ value b)

Единственные накладные расходы должны соответствовать конструктору Input.

(На вопросы о классе и дизайне экземпляров уже дан ответ, поэтому я не буду на них разбираться)

...