LINUX.ORG.RU

Особенности системы типов и расширения GHC

 , не anonimus,


0

3

Вероятно, на это уже давно есть ответ, но я даже не знаю как правильно загуглить.

Например, такой код:

import Data.List
 
main = print $ nub []

не компилируется. Причины понятны, решение (указать тип списка явно с любым типом элементов, для которого реализован, в данном случае, Eq) тоже.

Но это как-то криво, поэтому прошу подсказать, как можно реализовать (типизировать) nub (и любую другую подобную функцию), которая принимала бы пустой список произвольного типа, несмотря на то, что требует список элементов какого-то типа определённого тайпкласса. У length, например, проблем нет, т.к. она не ограничивает тип элементов тайпклассом.

P.S. Typed Racket, например, такое легко позволяет (хотя автоматически вывести тип для nub не может):

(: nub (All (a) (-> (Listof a) Any)))
(define (nub xs)
  (match xs
    ((list) #t)
    ((cons x xs) (and (not (member x xs))
                      (nub xs)))))

(print (nub '())) ;=> #t

Тут, правда, и тайпклассов нет. =)

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

★★★★★
Ответ на: комментарий от hateyoufeel

Не благодари :)

Договорились.

korvin_ ★★★★★
() автор топика

Причина почему это сделать не так просто как ты хочешь:

Prelude Data.List> print (nub [] :: [Int])
[]
Prelude Data.List> print (nub [] :: [Char])
""

Причины понятны, решение (указать тип списка явно с любым типом элементов, для которого реализован, в данном случае, Eq) тоже.

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

Но это как-то криво,

знать компилятору какой словарь использовать для сравнения и вывода данных это не криво.

Typed Racket, например, такое легко позволяет (хотя автоматически вывести тип для nub не может):

В Typed Racket есть типы, для которых не определено равенство, если есть то можно продолжать говорить. В данном случае, это тоже самое, что сделать data F = forall a. (Typeable a,Eq a, Show a) => F a

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

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от qnikst

Причина почему это сделать не так просто как ты хочешь:

Prelude Data.List> print (nub [] :: [Int])
[]
Prelude Data.List> print (nub [] :: [Char])
""

Как там поживает opaque string proposal?

hateyoufeel ★★★★★
()
Ответ на: комментарий от qnikst

В haskell-cafe поднималась эта тема вроде как. Вообще, type String = [Char] - это весьма мерзкое legacy, от которого было бы неплохо избавиться.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Получится же хуже, чем третий пистон.

Deleted
()
Ответ на: комментарий от hateyoufeel

ох.. там же ад какой-то предлагали, вообще иметь по дефолту (Lazy?) Text вместо String было бы круто. Но т.к. avoid-ить success всеж не удалось, то взять и всю совместимость сломать нельзя, а хороших решений вроде так и не предложили, чтобы ломать надо.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

postman_: в прошлый раз BBP прокатил.

вообще иметь по дефолту (Lazy?) Text вместо String было бы круто. Но т.к. avoid-ить success всеж не удалось, то взять и всю совместимость сломать нельзя, а хороших решений вроде так и не предложили, чтобы ломать надо.

Можно для начала попробовать сделать base с type String = Lazy.Text ради эксперимента.

К тому же, в MRP-треде предлагали в очередной раз всё сломать когда наберётся куча мелких изменений. MRP + OSP (Opaque String) - чем не кандидат?

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Последствия от невозможности заматчить строку по (:) будут сильнее, чем BBP, мне кажется. И у меня до сих пор 7.6.3, например.

Deleted
()
Ответ на: комментарий от qnikst

Причина почему это сделать не так просто как ты хочешь:

Prelude Data.List> print (nub [] :: [Int])
[]
Prelude Data.List> print (nub [] :: [Char])
""

Окей, для понятности, упростим: пусть nub возвращает только True|False, типа того:

isNub (x:xs)   = x `notElem` xs && isNub xs
isNub _        = True

Суть не в этом, а в том, что Хаскелл не даёт так просто применить произвольную функцию, ожидающую список, к пустому списку, если эта функция ожидает, что элементы списка соответствую какому-нибудь тайпклассу. Т.е., получая литерал [] Хаскелл не может определить тип элементов списка (т.к. он не известен) и соответственно не может определить, реализован ли для этого типа тайпкласс. С length нет проблем, т.к. её не интересуют типы элементов и их тайпклассы. Парадокс в том, что любой пустой список [] долже удовлетворять любому тайпклассу. Как числовые литералы подставляются куда угодно, а конкретный тип получают уже в процессе использования.

korvin_ ★★★★★
() автор топика
Ответ на: комментарий от korvin_

Пустой список не может «удовлетворять тайпклассу», тайпклассам принадлежат типы, а не значения. А пустой список — это не тип.

Miguel ★★★★★
()
Ответ на: комментарий от Bad_ptr

Значение же. Причём разное для разных типов:

Idris> tail [1]
[] : List Integer
Idris> tail ['C']
[] : List Char

quantum-troll ★★★★★
()

haskell98/2010 не позволяет описывать разные ограничения типов на разные конструкторы ((:) и []) для одного типа.

Но расширение GADTs как раз для этого:

{-# LANGUAGE GADTs, LambdaCase #-}
module Main where

import Data.List

data List a where
    Empty    :: List a
    NonEmpty :: (Eq a, Show a) => [a] -> List a

nubL :: List a -> List a
nubL = \case
    Empty      -> Empty
    NonEmpty l -> NonEmpty (nub l)

instance Show (List a) where
    show = \case
        Empty      -> "Empty"
        NonEmpty l -> "NonEmpty " ++ show l

main = do
    print $ nubL Empty
    print $ nubL (Empty :: List (a -> b))
    print $ nubL $ NonEmpty [1,2,1]
$ runhaskell a.hs
Empty
Empty
NonEmpty [1,2]
sf ★★★
()
Ответ на: комментарий от korvin_

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

суть в том, что пустой список не отличается от непустого списка по типу. Соотсветсвенно нужно их уметь отличать, например вводя GADT как ниже, или списки индексированные длиной (но этот путь в хацкеле требует хорошего уровня Type-Foo).

Парадокс в том, что любой пустой список [] долже удовлетворять любому тайпклассу.

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

qnikst ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.