Есть ли тип "Any" в haskell?
Скажем, я хочу определить атрибут записи следующим образом:
data Attribute = Attribute {name :: String, value :: Any}
Это, конечно, неверный код haskell. Но есть ли тип "Любой", который в принципе скажет, что любой тип будет делать? Или использовать переменную типа единственный способ?
data Attribute a = Attribute {name :: String, value :: a}
Ответы
Ответ 1
Вообще говоря, типы Any
не очень полезны. Рассмотрите: если вы делаете полиморфный список, который может содержать что-либо, что вы можете делать с типами в списке? Ответ, конечно, ничто - у вас нет гарантии, что для этих элементов существует какая-либо операция.
Что обычно будет делать:
-
Используйте GADTs, чтобы создать список, который может содержать элементы определенного типа, например:
data FooWrap where
FooWrap :: Foo a => a -> FooWrap
type FooList = [FooWrap]
При таком подходе вы не знаете конкретного типа элементов, но знаете, что их можно манипулировать с помощью элементов класса Foo
.
-
Создайте тип для переключения между конкретными конкретными типами, содержащимися в списке:
data FooElem = ElemFoo Foo | ElemBar Bar
type FooList = [FooElem]
Это можно объединить с подходом 1, чтобы создать список, который может содержать элементы, которые имеют один из фиксированных наборов типов.
-
В некоторых случаях может быть полезно создать список функций манипуляции:
type FooList = [Int -> IO ()]
Это полезно для таких вещей, как системы уведомлений о событиях. Во время добавления элемента в список вы привязываете его к функции, которая выполняет любые манипуляции, которые вы позже захотите сделать.
-
Используйте Data.Dynamic
(не рекомендуется!) как обманщик. Однако это не гарантирует, что какой-либо конкретный элемент можно вообще манипулировать, и поэтому предпочтительные подходы должны быть предпочтительными.
Ответ 2
Добавление в ответ bdonlan: вместо GADT вы также можете использовать экзистенциальные типы:
{-# LANGUAGE ExistentialQuantification #-}
class Foo a where
foo :: a -> a
data AnyFoo = forall a. Foo a => AnyFoo a
instance Foo AnyFoo where
foo (AnyFoo a) = AnyFoo $ foo a
mapFoo :: [AnyFoo] -> [AnyFoo]
mapFoo = map foo
Это в основном эквивалентно решению bdonlan GADT, но не налагает на вас выбор структуры данных. Вместо списка вы можете использовать Map
, например:
import qualified Data.Map as M
mFoo :: M.Map String AnyFoo
mFoo = M.fromList [("a", AnyFoo SomeFoo), ("b", AnyFoo SomeBar)]
Бит data AnyFoo = forall a. Foo a => AnyFoo a
также может быть записан в нотации GADT как:
data AnyFoo where
AnyFoo :: Foo a => a -> AnyFoo
Ответ 3
Существует тип Dynamic
из Data.Dynamic
, который может содержать что угодно (ну, что-нибудь Typeable
). Но это редко бывает правильным способом. В чем проблема, которую вы пытаетесь решить?
Ответ 4
Это звучит как довольно простой вопрос, поэтому я собираюсь дать более общий ответ, чем кто-либо другой. Здесь почти всегда правильное решение:
data Attribute a = Attribute { name :: String, value :: a }
Затем, если вам нужен атрибут, который обертывает Int
, этот атрибут будет иметь тип Attribute Int
, или атрибут, который обертывает Bool
, будет иметь тип Attribute Bool
и т.д. Вы можете создать эти атрибуты с помощью значения любого типа; например, мы можем написать
testAttr = Attribute { name = "this is only a test", value = Node 3 [] }
чтобы создать значение типа Attribute (Tree Int)
.
Ответ 5
Если ваши данные должны быть в конечном итоге конкретным типом, вы можете использовать Convertible с GADT. Потому что, как потребитель, вас интересует только тип данных, который вам нужно потреблять.
{-# LANGUAGE GADTs #-}
import Data.Convertible
data Conv b where
Conv :: a -> (a -> b) -> Conv b
Chain :: Conv b -> (b -> c) -> Conv c
unconv :: (Conv b) -> b
unconv (Conv a f) = f a
unconv (Chain c f) = f $ unconv c
conv :: Convertible a b => a -> Conv b
conv a = (Conv a convert)
totype :: Convertible b c => Conv b -> Conv c
totype a = Chain a convert
Не так сложно получить для этого функторы, экземпляры comonad и monad. Я могу опубликовать их, если вы заинтересованы.