Почему функции floatRange, floatRadix и floatDigits? - PullRequest
0 голосов
/ 15 октября 2018

Согласно Hackage , эти функции класса RealFloat являются

... константа [s] ...

Если они всегда остаются на одном и том же значении, независимо от аргумента, как предлагается в этом описании, почему бы просто не использовать:

class (RealFrac a, Floating a) => RealFloat a where
    floatRadix :: Integer
    floatDigits :: Int
    floatRange :: (Int, Int)
    ...

Ответы [ 3 ]

0 голосов
/ 15 октября 2018

Предложенные нефункциональные методы будут иметь тип

floatRadix' :: RealFloat a => Integer
floatDigits' :: RealFloat a => Int
...

Это неоднозначные типы : есть переменная типа a, но на самом деле она не отображаетсяправо на => и, следовательно, не может быть выведено из контекста.Что, в стандартном Haskell, действительно only способ, которым вы можете вывести такую ​​переменную типа: локальные сигнатуры типа также могут быть только для заголовка сигнатуры, но не для ограничения.поэтому, пишете ли вы (floatDigits' :: Int) или (floatDigits' :: RealFloat Double => Int), на самом деле это не сработает - компилятор не может сделать вывод, что вы имеете в виду версию метода instance RealFloat Double.

class RealFloat' a where
  floatDigits' :: Int
instance RealFloat' Double where
  floatDigits' = floatDigits (0 :: Double)
*Main> floatDigits' :: Int

<interactive>:3:1: error:
    • No instance for (RealFloat' a0)
        arising from a use of ‘floatDigits'’
    • In the expression: floatDigits' :: Int
      In an equation for ‘it’: it = floatDigits' :: Int
*Main> floatDigits' :: RealFloat Double => Int

<interactive>:4:1: error:
    • Could not deduce (RealFloat' a0)
        arising from a use of ‘floatDigits'’
      from the context: RealFloat Double
        bound by an expression type signature:
                   RealFloat Double => Int
        at :4:17-39
      The type variable ‘a0’ is ambiguous
    • In the expression: floatDigits' :: RealFloat Double => Int
      In an equation for ‘it’:
          it = floatDigits' :: RealFloat Double => Int

ДляПо этой причине Haskell не позволяет вам писать методы с неоднозначным типом.Фактически, попытка скомпилировать класс, как я написал выше, выдает следующее сообщение об ошибке:

• Could not deduce (RealFloat' a0)
      from the context: RealFloat' a
        bound by the type signature for:
                   floatDigits' :: forall a. RealFloat' a => Int
        at /tmp/wtmpf-file3738.hs:2:3-21
      The type variable ‘a0’ is ambiguous
    • In the ambiguity check for ‘floatDigits'’
      <b>To defer the ambiguity check to use sites, enable AllowAmbiguousTypes</b>
      When checking the class method:
        floatDigits' :: forall a. RealFloat' a => Int
      In the class declaration for ‘RealFloat'’

Однако выделенная строка указывает на расширение GHC, которое говорит: «все в порядке, я знаю, что делаю».Поэтому, если вы добавите {-# LANGUAGE AllowAmbiguousTypes #-} в начало файла с class RealFloat' в нем, компилятор примет это.

Какой смысл, когда экземпляр не можетразрешаться при использовании сайта?Ну, это можно на самом деле решить, но только с использованием другого довольно нового расширения GHC :

*Main> :set -XTypeApplications 
*Main> floatDigits' @Double
53
0 голосов
/ 15 октября 2018

Класс RealFloat очень старый.Когда он был спроектирован, никто не нашел действительно хороших способов передачи дополнительной информации о типе в функцию.В то время было принято принимать аргументы соответствующего типа и ожидать, что пользователь передаст undefined для этого типа.Как пояснил leftaround, у GHC теперь есть расширения, которые делают это довольно хорошо в большинстве случаев.Но до TypeApplications были изобретены два других метода, позволяющих сделать эту работу более чистой.

Чтобы устранить неоднозначность без расширений GHC, вы можете использовать либо передачу через прокси, либо тегирование на основе нового типа.Я полагаю, что обе техники получили свои окончательные формы Эдвардом Кметтом с последним полиморфным вращением Шафафа Бен-Кики (см. Кто изобрел передачу прокси и когда? ).Передача через прокси дает простой в использовании API, в то время как подход нового типа может быть более эффективным при определенных обстоятельствах.Вот подход передачи по доверенности.Это требует, чтобы вы передали аргумент некоторого типа.Традиционно вызывающая сторона будет использовать Data.Proxy.Proxy, который определен

data Proxy a = Proxy

Вот как класс будет выглядеть с передачей прокси:

class (RealFrac a, Floating a) => RealFloat a where
    floatRadix :: proxy a -> Integer
    floatDigits :: proxy a -> Int
    ...

А вот как это будет использоваться.Обратите внимание, что нет необходимости передавать значение типа, о котором вы говорите;Вы просто передаете прокси-конструктор.

foo :: Int
foo = floatDigits (Proxy :: Proxy Double)

Чтобы вообще не передавать аргумент времени выполнения, вы можете использовать тегирование.Это часто делается с пакетом tagged, но его тоже довольно просто свернуть.Вы могли бы даже повторно использовать Control.Applicative.Const, но это не очень хорошо передает намерение.

newtype Tagged t a = Tagged
  { unTagged :: a }

Вот как будет выглядеть класс:

class (RealFrac a, Floating a) => RealFloat a where
    floatRadix :: Tagged a Integer
    floatDigits :: Tagged a Int
    ...

А вот какиспользуйте это:

foo :: Int
foo = unTagged (floatDigits :: Tagged Double Int)
0 голосов
/ 15 октября 2018

Проблема в том, что вы должны построить функции для нескольких экземпляров, например:

instance RealFloat Float where
    -- ...
    floatRadix = 2
    floatDigits = 24
    floatRange = (-125, 128)

instance RealFloat Double where
    -- ...
    floatRadix = 2
    floatDigits = 53
    floatRange = (-1021, 1024)

Но теперь это создает проблему, когда вы запрашиваете, например, floatDigits: для какого экземпляра мы должны взять?Один для Float или один для Double (или другой тип)?Все они являются действительными кандидатами.

Используя параметр a, мы можем сделать устранение неоднозначности, например:

Prelude> floatDigits (0 :: Float)
24
Prelude> floatDigits (0 :: Double)
53

, но в нем указано, что значение параметра невопрос, например:

Prelude> floatDigits (undefined :: Float)
24
Prelude> floatDigits (undefined :: Double)
53
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...