«сопоставление с образцом» для типов Typeable

Предположим, например, что у нас есть следующая структура данных:

data Foo = Bool Bool | Int Int | Double Double

Теперь, есть ли более простой способ сделать это:

foo :: Typeable a => a -> Foo
foo x = maybe (error "i dunno") id $
  liftM Bool   (cast x) `mplus`
  liftM Int    (cast x) `mplus`
  liftM Double (cast x)

Кто-нибудь думал о создании синтаксиса для сопоставления с образцом для типов Typeable?


person Community    schedule 04.05.2011    source источник


Ответы (3)


Используйте typeOf и охранников:

foo x
    | tx == typeOf "str" = "string"
    | tx == typeOf True  = "bool"
    | otherwise          = "i dunno"
  where tx = typeOf x
person sclv    schedule 04.05.2011
comment
Я думаю, что это хороший подход. Один незначительный момент заключается в том, что typeOf str не будет хорошо работать при наличии OverloadedStrings, что становится все более распространенным в наши дни. - person Michael Snoyman; 04.05.2011
comment
Аналогичная проблема с числовыми литералами. Он также не масштабируется до ситуаций, когда у вас нет конструкторов для типа данных/они не являются чистыми. Вскоре вы получите (undefined :: Foo), что ужасно. Из-за этого мне не нравится это решение. - person Tener; 05.05.2011
comment
Шрифт должен быть немного некрасивым. Это напоминает нам вернуться к программированию с реальными типами и перестать возиться со всей этой кашей во время выполнения. - person sclv; 05.05.2011

Здесь вы можете использовать шаблоны просмотра, это очень удобное расширение.

{-# LANGUAGE ViewPatterns #-}

import Data.Data

data Foo = Bool Bool | Int Int | Double Double
         deriving (Show)

foo :: Typeable a => a -> Foo
foo (cast -> Just x) = Int x
foo (cast -> Just x) = Bool x
foo (cast -> Just x) = Double x
foo _ = error "i dunno"

Полученные результаты:

*Main> :l foo_typeable.hs 
[1 of 1] Compiling Main             ( foo_typeable.hs, interpreted )
Ok, modules loaded: Main.
*Main> foo "123"
*** Exception: i dunno
*Main> foo 1
*** Exception: i dunno
*Main> foo (1 :: Int)
Int 1
*Main> foo (1 :: Integer)
*** Exception: i dunno
*Main> foo (1 :: Double)
Double 1.0
person Tener    schedule 04.05.2011
comment
Это довольно аккуратное решение! - person ; 05.05.2011

Эта версия не ограничивается Bool, Int или Double, но String получается как [Char].

foo :: Typeable a => a -> String
foo = show . typeOf
person pat    schedule 04.05.2011
comment
... что разумно, поскольку String - это просто псевдоним типа для [Char]. - person hammar; 04.05.2011