Приложение частичной функции Haskell с $

Я новичок в Haskell и смотрю на простой пример использования приложения-функции с $.

Это кажется простым — он берет функцию и применяет ее к значению.

Так что это имеет смысл:

> (+3) $ 2
5

Это также имеет смысл:

> ($) (+3) 2
5

Это имеет смысл, потому что первый аргумент — это функция, а второй — значение.

Теперь рассмотрим возможность использования $ для создания частичной функции.

Глядя на типы, это имеет смысл - просто нужно значение типа Num для b:

> :t ($) (+3)
($) (+3) :: Num b => b -> b

Но вот где я теряюсь - что здесь происходит?:

> :t ($) (2)
($) (2) :: Num (a -> b) => a -> b

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

Итак, вот мои вопросы:

  1. Что тут происходит?
  2. Что означает синтаксис ограничения Num (a -> b)?
  3. Какой пример использования ($) таким образом, который начинается с чего-то вроде ($) (2)?

Спасибо!


person Kevan Stannard    schedule 02.02.2019    source источник
comment
Числовые литералы в Haskell не имеют фиксированных типов. Попробуйте :t 2 и посмотрите. 2 может быть функцией, если очень постараться.   -  person n. 1.8e9-where's-my-share m.    schedule 02.02.2019


Ответы (1)


С одной стороны, числовые литералы, такие как 2, на самом деле читаются как fromInteger 2 :: Num a => a, поэтому могут обозначать любое значение типа Num a => a, то есть любой тип, который в классе типов Num, т. е. среди прочего имеет специальную версию fromInteger определено, которое возвращает фактическое значение фактического типа, преобразованное из целого числа 2:

> :i Num
class Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a

Как сказано в учебнике по Haskell (в 10.3),

Целочисленное число (без десятичной точки) фактически эквивалентно применению fromInteger к значению числа как Integer.

С другой стороны, ($) имеет тип

> :t ($)
($) :: (a -> b) -> a -> b

Итак, у нас есть

fromInteger 2 :: Num a1 =>   a1
($)           ::          (a -> b) -> a -> b
--------------------------------------------
($) 2         :: Num      (a -> b) => a -> b

Итак, это функция, которая также должна быть в классе типов Num.

Обычно это не так, но Haskell не знает, можете ли вы импортировать какой-либо модуль, который определяет такой экземпляр:

instance Num (a -> b) where
   ....
   fromInteger n = ....
   ....

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

Например, следуя подсказке от @augustss в комментариях,

instance (Num b) => Num (a -> b) where
   (+) f g x = f x + g x
   (*) f g x = f x * g x
   abs f x = abs (f x)
   negate f x = negate (f x)
   signum f x = signum (f x)
   fromInteger n = const (fromInteger n)

давайте напишем (sin + 2 * cos^2) x.

person Will Ness    schedule 02.02.2019
comment
И создание a->b экземпляра Num может быть весьма полезным, если b также является экземпляром Num. - person augustss; 02.02.2019
comment
не могли бы вы подсказать почему и где? - person Will Ness; 02.02.2019
comment
Попробуй. :) Вы сможете писать такие вещи, как (sin + 2 * cos) x вместо sin x + 2 * cos x. Написать объявление экземпляра несложно. Типы заставят вас принять правильное определение. - person augustss; 02.02.2019
comment
благодаря. ага, так (cos^2) x = (cos x)^2. Я зациклился на cos^2 = cos . cos и немного озадачился этим... - person Will Ness; 02.02.2019
comment
Спасибо за подробное объяснение и пример! - person Kevan Stannard; 02.02.2019
comment
пожалуйста. спасибо за вопрос и оставайтесь на связи! :) - person Will Ness; 02.02.2019