Подтвердить что ты не робот

Предотвращение загрязнения пространства имен в Haskell

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

data Customer = Customer { ..., foo :: Int, ... }
data Product = Product { ..., foo :: Int, ... }

Теперь, когда функция доступа "foo" определена дважды, я получаю ошибку "Несколько деклараций". Один из способов избежать этого - использовать различные модули, которые импортируются полностью, или просто переименовывать поля (которые я не хочу делать).

Каков официально предложенный способ справиться с этим в Haskell?

4b9b3361

Ответ 1

Это очень волосатая проблема. Существует несколько предложений по фиксации системы записи. О соответствующем примечании см. TDNR и связанная дискуссия о кафе,

Используя доступные в настоящее время функции языка, я считаю, что лучшим вариантом является определение двух типов в двух разных модулях и выполнение квалифицированного импорта. Кроме того, если вы хотите, вы можете реализовать некоторые машины класса типов.

В Customer.hs

module Customer where
data Customer = Customer { ..., foo :: Int, ... }

В Product.hs

module Product where
data Product = Product { ..., foo :: Int, ... }

При использовании их в Third.hs

module Third where

import qualified Customer as C
import qualified Product as P

.. C.foo ..
.. P.foo ..

Тем не менее, я полагаю, что не будет слишком поздно, прежде чем вы столкнетесь с проблемой рекурсивно зависимых модулей.

Ответ 2

(FYI, этот вопрос почти наверняка является дубликатом)

Решения:

1) Префикс полей тегом, указывающим тип (чрезвычайно распространенный)

data Customer = Customer {..., cFoo :: Int, ...}

2) Использовать классы типов (менее распространенные, люди, жалующиеся на префиксы, такие как cFoo, неудобны, но, очевидно, не так уж плохо, что они будут писать класс и экземпляр или использовать TH, чтобы сделать то же самое).

class getFoo a where
    foo :: a -> Int

instance getFoo Customer where
    foo = cFoo

3) Используйте лучшие имена полей Если поля действительно разные (это не всегда так, мой компьютер имеет возраст, как и мой сотрудник), то это лучшее решение.

Ответ 3

См. также пакет Has: http://chrisdone.com/posts/duck-typing-in-haskell

И если вам действительно нужны расширенные записи, вы всегда можете использовать HList. Но я бы не рекомендовал это, пока вы действительно не знакомы и не комфортно со средним продвинутым Haskell, и даже тогда я бы трижды проверил, что вам это нужно.

Haskelldb имеет немного более легкую версию: http://hackage.haskell.org/packages/archive/haskelldb/2.1.0/doc/html/Database-HaskellDB-HDBRec.html

И вот еще одна версия расширяемых записей как часть библиотеки frp грейпфрута: http://hackage.haskell.org/package/grapefruit-records

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

Ответ 4

Имеется расширение языка DuplicateRecordFields, которое позволяет дублировать функции поля и выводит его тип по типу аннотации.

Вот небольшой пример (haskell-stack script):

#!/usr/bin/env stack
-- stack runghc --resolver lts-8.20 --install-ghc

{-# LANGUAGE DuplicateRecordFields #-}

newtype Foo = Foo { baz :: String }
newtype Bar = Bar { baz :: String }

foo = Foo { baz = "foo text" }
bar = Bar { baz = "bar text" }

main = do
  putStrLn $ "Foo: " ++ baz (foo :: Foo) -- Foo: foo text
  putStrLn $ "Bar: " ++ baz (bar :: Bar) -- Bar: bar text