Как избежать загрязнения пространства имен в Хаскеле - PullRequest
53 голосов
/ 23 ноября 2010

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

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

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

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

Ответы [ 5 ]

23 голосов
/ 24 ноября 2010

Это очень волосатая проблема.Есть несколько предложений по исправлению системы записи.См. Примечание по теме 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 ..

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

12 голосов
/ 24 ноября 2010

(К вашему сведению, этот вопрос почти наверняка повторяется)

Решения:

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

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

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

class getFoo a where
    foo :: a -> Int

instance getFoo Customer where
    foo = cFoo

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

7 голосов
/ 24 ноября 2010

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

И если вам действительно нужны расширяемые записи сейчас, вы всегда можете использовать HList.Но я бы не советовал это, пока вы действительно не знакомы и не знакомы со средне-продвинутым Haskell, и даже тогда я бы трижды проверил, что оно вам нужно.http://hackage.haskell.org/packages/archive/haskelldb/2.1.0/doc/html/Database-HaskellDB-HDBRec.html

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

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

6 голосов
/ 24 июня 2017

Существует расширение языка 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
0 голосов
/ 10 февраля 2019

Одним из возможных решений, которое сделает ваш код менее многословным, является определение <.> как:

(<.>) :: (Emiter e1, Emiter e2) => e1 -> e2 -> String
lhs <.> rhs = emit lhs <> emit rhs

Тогда излучатели могут выглядеть так:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...