«сопоставление шаблонов» для типизируемых типов

#haskell #syntax #types #dynamic-typing

#haskell #синтаксис #типы #динамическая типизация

Вопрос:

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

 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)
  

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

Ответ №1:

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

 foo x
    | tx == typeOf "str" = "string"
    | tx == typeOf True  = "bool"
    | otherwise          = "i dunno"
  where tx = typeOf x
  

Комментарии:

1. Я думаю, что это хороший подход. Один незначительный момент заключается в том, что typeOf «str» не будет хорошо работать при наличии перегруженных строк, что в наши дни становится все более распространенным явлением.

2. Аналогичная проблема возникает с числовыми литералами. Это также не масштабируется до ситуаций, когда у вас нет конструкторов для типа данных / они не являются чистыми. В конечном итоге вы получили бы (undefined :: Foo) слишком долго, что уродливо. Из-за этого мне не нравится это решение.

3. Набор типов должен быть немного уродливым. Это напоминает нам вернуться к программированию с реальными типами и прекратить возиться со всей этой мешаниной во время выполнения.

Ответ №2:

Вы можете использовать шаблоны просмотра здесь, они являются довольно удобным расширением.

 {-# 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"
  

Результаты:

*Главная> :l foo_typeable.hs 
[1 из 1] Компиляция Main ( foo_typeable.hs, интерпретируется)
Хорошо, загруженные модули: Main.
*Main> foo "123"
*** Исключение: я не знаю
*Главная> foo 1
*** Исключение: я не знаю
*Main> foo (1 :: Int)
Int 1
*Main> foo (1 :: Целое число)
*** Исключение: я не знаю
*Main> foo (1 :: Double)
Double 1.0

Комментарии:

1. Это довольно изящное решение!

Ответ №3:

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

 foo :: Typeable a => a -> String
foo = show . typeOf
  

Комментарии:

1. … что разумно, поскольку String это просто псевдоним типа для [Char] .