Может ли тип (a == b) быть полиморфным? - PullRequest
2 голосов
/ 11 июня 2019

Так как (==) :: Eq a => a -> a -> Bool, я бы ожидал

a == b :: Eq a => Bool
-- assuming a :: forall a. a
-- derived type

но (согласно GHCi :t)

a == b :: Bool

Теперь, конечно, для этого требуется TypeApplications, поэтому он не должен быть включен по умолчанию, но есть опция LANGUAGE, которая включает это?

NoMonomorphismRestriction не работает.

РЕДАКТИРОВАТЬ: Поскольку вы можете сказать, «хорошо, GHC (i) уже знает тип a и b в любом практическом примере», нет, например, вы могли бы иметь (5 == 5) :: (Num a, Eq a) => Bool.
РЕДАКТИРОВАТЬ: кажется, a :: forall a. a невозможно, поэтому давайте предположим, что-то вроде x = 5 :: Num a => a

РЕДАКТИРОВАТЬ: Явное объявление типа работает , поэтому вопрос на самом деле "Может ли GHC сделать этот тип полиморфным", поэтому я ищу что-то вроде DeriveAmbiguousTypes.

Ответы [ 3 ]

4 голосов
/ 11 июня 2019

Это все о типе (==):

(==) :: forall a. Eq a => a -> a -> Bool

(==) принимает четыре аргумента, в порядке :

  1. Тип A, a, типа Type (AKA *),
  2. Словарь типа Eq a,
  3. Значение типа a.
  4. Значение типа a.

Как обычно, частичное приложение работает только в порядке , и словарные аргументы не могут быть переданы явно.Кроме того, аргументы типа могут быть введены только до их использования.Поэтому, когда вы пишете a == b, это на самом деле означает

(==) @_ @{dict} a b

Аргумент типа и словарный аргумент выводятся, и вы получаете значение типа Bool.

Что выafter имеет тип, весьма отличающийся от (==):

numLitEq
  :: (forall a. (Num a, Eq a) => a)
  -> (forall b. (Num b, Eq b) => b)
  -> forall c. (Num c, Eq c) => Bool
numLitEq a b = (==) @c a b

Однако вы не можете написать именно это, потому что нет способа получить переменную типа c в область видимости.Лучшее, что вы можете сделать, это

numLitEq
  :: forall c.
     (forall a. (Num a, Eq a) => a)
  -> (forall b. (Num b, Eq b) => b)
  -> (Num c, Eq c) => Bool
numLitEq a b = (==) @c a b

, что не лучше, чем просто использование (==).

1 голос
/ 12 июня 2019

Обратите внимание, что если вы просто спрашиваете, можно ли сделать выражение 3 == 5 полиморфным в типах 3 и 5, ответ - да.Подпись должна быть дана явно с помощью некоторых языковых расширений, но следующее определение является полиморфным:

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module ThreeFive where

eq35 :: forall a . (Num a, Eq a) => Bool
eq35 = (3 :: a) == 5

В качестве альтернативы, вы можете использовать TypeApplications:

eq35' :: forall a . (Num a, Eq a) => Bool
eq35' = (==) @a 3 5

Теперь,GHCi сообщает тип eq35 как Bool, но это ложь, как вы можете видеть, добавив флаг +v:

> :type eq35
eq35 :: Bool
> :type +v eq35
eq35 :: (Num a, Eq a) => Bool

или доказав это:

{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- Holistic number are all equal
newtype Holistic = Holistic Int deriving (Num)
instance Eq Holistic where
  _ == _ = True

main = do print (eq35 @Int)
          print (eq35 @Holistic)

и запуск этой команды выведет False и True.

Я не вижу никакой комбинации языковых расширений, которая позволила бы GHC автоматически выводит более общий тип для 3 == 5, чем True при отсутствии явных сигнатур типов.Точно так же, как бы полиморфными не были x и y, x == y будет либо по умолчанию установлен в мономорфный тип, либо отклонен, если вы не используете явную подпись, подобную приведенной выше.

1 голос
/ 11 июня 2019

На самом деле это просто работает - как минимум в GHC-8.2 и 8.3.

GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
Prelude> :set -XRank2Types -XUnicodeSyntax
Prelude> let x, y :: ∀ a . a; (x,y) = (undefined,undefined)
Prelude> :set -XAllowAmbiguousTypes 
Prelude> let p :: ∀ a . Eq a => Bool; p = x==y

Prelude> :set -XTypeApplications 

Prelude> p @Int
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
  error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err
  undefined, called at <interactive>:3:36 in interactive:Ghci1

Prelude> p @(String -> Double)
<interactive>:11:1: error:
    • No instance for (Eq (String -> Double)) arising from a use of ‘p’
        (maybe you haven't applied a function to enough arguments?)
    • In the expression: p @(String -> Double)
      In an equation for ‘it’: it = p @(String -> Double)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...