Если у вас разные типы данных, которые ведут себя по-разному, но несколько одинаковы (например, имеют те же методы, но отличаются друг от друга), тогда это классы Haskell для спасения!
data A = A { nameA :: String } deriving (Show, Eq)
data B = B { nameB :: String } deriving (Show, Eq)
class Named a where
getName :: a -> String
instance Named A where
getName = nameA
instance Named B where
getName = nameB
ghci> getName $ A "foo"
"foo"
ghci> getName $ B "bar"
"bar"
(Если вы еще не видели синтаксис записи еще, есть good explanation at LYAH)
Вы можете даже быть более общим, чем это.
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
-- The above line should be at the top of your file
-- add this to the file too (anywhere)
instance (Show a) => Named a where
getName = show
ghci> getName 3
"3"
ghci> show 3
"3"
ghci> getName $ A "foo"
"foo"
ghci> show $ A "foo"
"A {nameA = \"foo\"}"
Язык Прагма OverlappingInstances необходим только в этом примере, если вы держите как Instance (Show a) => Named a
и instance Named A
, так как я сделал A
экземпляр Show
, выводя его. Используйте языковые прагмы с осторожностью и мудростью.
Я сделал A
и B
данные для иллюстративных целей, но пример (Show a)
иллюстрирует, как это можно сделать так же легко для классов.
@ok, поэтому вы имеете в виду, что я должен использовать разные имена или использовать Show, когда речь идет о String. Как насчет использования newType, чтобы квалифицировать строки так, как они несовместимы, если они означают совершенно разные вещи? Обычное использование newType? –
@stephane Представьте себе тип 'T' (любой тип, это может быть новый тип), и теперь представьте, что у вас есть экземпляр AT где ...' и экземпляр BT, где ... 'Как вы знаете, какая функция для запускать, когда кто-то вызывает 'getName'? –
@Stephane: Как всегда, это зависит. Если строки представляют концептуально разные вещи, я бы выбрал этот способ (если только сделать подпись типа более выразительной: 'xyz :: thing -> ThingID' вместо' xyz :: thing -> String') – delnan