[Закрываются] скрытые функции Haskell

Указатель NULL - это тот, который указывает на никуда. Когда вы разыскиваете указатель p, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является нулевым указателем, местоположение, хранящееся в p, является nowhere, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.

32
задан 4 revs, 3 users 70% 6 May 2012 в 17:45
поделиться

25 ответов

Мой мозг просто взорвался

, При попытке скомпилировать этот код:

{-# LANGUAGE ExistentialQuantification #-}
data Foo = forall a. Foo a
ignorefoo f = 1 where Foo a = f

Вы получите это сообщение об ошибке:

$ ghc Foo.hs

Foo.hs:3:22:
    My brain just exploded.
    I can't handle pattern bindings for existentially-quantified constructors.
    Instead, use a case-expression, or do-notation, to unpack the constructor.
    In the binding group for
        Foo a
    In a pattern binding: Foo a = f
    In the definition of `ignorefoo':
        ignorefoo f = 1
                    where
                        Foo a = f
25
ответ дан 27 November 2019 в 19:38
поделиться

Симпатичная защита

Prelude определяет otherwise = True, делая полное защитное чтение условий очень естественно.

fac n
  | n < 1     = 1
  | otherwise = n * fac (n-1)
15
ответ дан 27 November 2019 в 19:38
поделиться

Гибкая спецификация импорта модуля и экспорта

Импорт и экспорт хороши.

module Foo (module Bar, blah)  -- this is module Foo, export everything that Bar expored, plus blah

import qualified Some.Long.Name as Short
import Some.Long.Name (name)  -- can import multiple times, with different options

import Baz hiding (blah)  -- import everything from Baz, except something named 'blah'
9
ответ дан 27 November 2019 в 19:38
поделиться

при поиске списка или функции высшего порядка это уже там

существует sooo многие удобство и функции высшего порядка в стандартной библиотеке.

-- factorial can be written, using the strict HOF foldl':
fac n = Data.List.foldl' (*) 1 [1..n]
-- there's a shortcut for that:
fac n = product [1..n]
-- and it can even be written pointfree:
fac = product . enumFromTo 1
8
ответ дан 27 November 2019 в 19:38
поделиться

круглые скобки Предотвращения

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

f (g (h x))
f $ g $ h x
f . g $ h x
f . g . h $ x

flip помогает также, следующее эквивалентны:

map (\a -> {- some long expression -}) list
flip map list $ \a ->
    {- some long expression -}
15
ответ дан 27 November 2019 в 19:38
поделиться

Читаемая композиция функций

Prelude определяет (.), чтобы быть составом математической функции; то есть, g . f первый применяется f, затем применяется g к результату.

, Если Вы import Control.Arrow, следующее эквивалентно:

g . f
f >>> g

Control.Arrow обеспечивает instance Arrow (->), и это хорошо для людей, которым не нравится читать функциональное приложение назад.

13
ответ дан 27 November 2019 в 19:38
поделиться

Списки Бога

, Так как Вы упомянули fibonacci, существует очень изящный способ генерирующиеся числа Фибоначчи из бесконечного списка как это:

fib@(1:tfib)    = 1 : 1 : [ a+b | (a,b) <- zip fib tfib ]

оператор позволяет Вам использовать сопоставление с образцом на 1:tfib структура, все еще называя целый шаблон выдумкой.

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

take 10 fib

можно также применить операцию ко всем элементам прежде, чем запросить их:

take 10 (map (\x -> x+1) fib)

Это благодаря отложенным вычислениям Haskell параметров и списков.

10
ответ дан 27 November 2019 в 19:38
поделиться

Неподвижность Оператора

можно использовать эти инфикс, infixl или infixr ключевые слова для определения ассоциативности операторов и приоритета. Пример взят от ссылка :

main = print (1 +++ 2 *** 3)

infixr  6 +++
infixr  7 ***,///

(+++) :: Int -> Int -> Int
a +++ b = a + 2*b

(***) :: Int -> Int -> Int
a *** b = a - 4*b

(///) :: Int -> Int -> Int
a /// b = 2*a - 3*b
Output: -19

число (от 0 до 9) после того, как инфикс позволяет Вам определять приоритет оператора, будучи 9 самое сильное. Инфикс не означает ассоциативности, тогда как оставленные партнеры infixl и infixr связывают право.

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

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

main = print (a `foo` b)

foo :: Int -> Int -> Int
foo a b = a + b

И как таковой, можно также определить приоритет для них:

infixr 4 `foo`
16
ответ дан 27 November 2019 в 19:38
поделиться

seq и ($!) только оценивают достаточно, чтобы проверить, что что-то не нижняя часть.

следующая программа только распечатает "там".

main = print "hi " `seq` print "there"

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

, Например, следующая "проигнорированная" печать и завершается с успехом.

main = foo (error "explode!")
  where foo _ = print "ignored"

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

, Например:

main = error "first" `seq` print "impossible to print"

... или эквивалентно, без инфикса...

main = seq (error "first") (print "impossible to print")

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

, Таким образом, это могло бы быть немного удивления, что даже при том, что seq строго, это не оценит что-то способ, которым оценивают нетерпеливые языки. В частности, это не попытается вызвать все положительные целые числа в следующей программе. Вместо этого это проверит, что [1..] не нижняя часть (который может быть сразу найден), печать, "сделанная", и выход.

main = [1..] `seq` print "done"
16
ответ дан 27 November 2019 в 19:38
поделиться

Дополнительное Расположение

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

let {
      x = 40;
      y = 2
     } in
 x + y

... или эквивалентно...

let { x = 40; y = 2 } in x + y

... вместо...

let x = 40
    y = 2
 in x + y

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

17
ответ дан 27 November 2019 в 19:38
поделиться

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

{-# LANGUAGE GADTs #-}
module Exp
where

data Exp a where
  Num  :: (Num a) => a -> Exp a
  Bool :: Bool -> Exp Bool
  Plus :: (Num a) => Exp a -> Exp a -> Exp a
  If   :: Exp Bool -> Exp a -> Exp a -> Exp a 
  Lt   :: (Num a, Ord a) => Exp a -> Exp a -> Exp Bool
  Lam  :: (a -> Exp b) -> Exp (a -> b)   -- higher order abstract syntax
  App  :: Exp (a -> b) -> Exp a -> Exp b
 -- deriving (Show) -- failse

eval :: Exp a -> a
eval (Num n)      = n
eval (Bool b)     = b
eval (Plus e1 e2) = eval e1 + eval e2
eval (If p t f)   = eval $ if eval p then t else f
eval (Lt e1 e2)   = eval e1 < eval e2
eval (Lam body)   = \x -> eval $ body x
eval (App f a)    = eval f $ eval a

instance Eq a => Eq (Exp a) where
  e1 == e2 = eval e1 == eval e2

instance Show (Exp a) where
  show e = "<exp>" -- very weak show instance

instance (Num a) => Num (Exp a) where
  fromInteger = Num
  (+) = Plus
19
ответ дан 27 November 2019 в 19:38
поделиться

комментарии .

мультилинии Nestable
{- inside a comment,
     {- inside another comment, -}
still commented! -}
19
ответ дан 27 November 2019 в 19:38
поделиться

Сокращение от общей операции списка

следующее эквивалентны:

concat $ map f list
concatMap f list
list >>= f

Редактирование

, Так как больше деталей требовали...

concat :: [[a]] -> [a]

concat берет список списков и связывает их в единственный список.

map :: (a -> b) -> [a] -> [b]

map карты функция по списку.

concatMap :: (a -> [b]) -> [a] -> [b]

concatMap эквивалентно [1 112]: отобразите функцию по списку и свяжите результаты.

class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
    return :: a -> m a

А Monad имеет , связывают операция, которую называют >>= в Haskell (или его в сахаре do - эквивалентной). Список, иначе [], Monad. Если мы занимаем место [] [1 119] в вышеупомянутом:

instance Monad [] where
    (>>=) :: [a] -> (a -> [b]) -> [b]
    return :: a -> [a]

, Какова естественная вещь для эти Monad операции, чтобы сделать в списке? Мы должны удовлетворить законы о монаде,

return a >>= f           ==  f a
ma >>= (\a -> return a)  ==  ma
(ma >>= f) >>= g         ==  ma >>= (\a -> f a >>= g)

можно проверить, что эти законы содержат, если мы используем реализацию

instance Monad [] where
    (>>=) = concatMap
    return = (:[])

return a >>= f  ==  [a] >>= f  ==  concatMap f [a]  ==  f a
ma >>= (\a -> return a)  ==  concatMap (\a -> [a]) ma  ==  ma
(ma >>= f) >>= g  ==  concatMap g (concatMap f ma)  ==  concatMap (concatMap g . f) ma  ==  ma >>= (\a -> f a >>= g)

, Это - на самом деле, поведение [1 121]. Как демонстрация,

double x = [x,x]
main = do
    print $ map double [1,2,3]
        -- [[1,1],[2,2],[3,3]]
    print . concat $ map double [1,2,3]
        -- [1,1,2,2,3,3]
    print $ concatMap double [1,2,3]
        -- [1,1,2,2,3,3]
    print $ [1,2,3] >>= double
        -- [1,1,2,2,3,3]
20
ответ дан 27 November 2019 в 19:38
поделиться

Пользовательские управляющие структуры

у Haskell нет краткого тернарного оператора. Встроенное if - then - else является всегда троичным, и является выражением (императивные языки имеют тенденцию иметь ?: =expression, if =statement). Если Вы захотите, тем не менее,

True ? x = const x
False ? _ = id

, то определит (?), чтобы быть тернарным оператором:

(a ? b $ c)  ==  (if a then b else c)

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

-- prints "I'm alive! :)"
main = True ? putStrLn "I'm alive! :)" $ error "I'm dead :("
40
ответ дан 27 November 2019 в 19:38
поделиться

Шаблоны в привязках верхнего уровня

five :: Int
Just five = Just 5

a, b, c :: Char
[a,b,c] = "abc"

Как это круто! Спасает вас тот звонок на из Просто и голову то и дело.

18
ответ дан 27 November 2019 в 19:38
поделиться

Hoogle

Hoogle - ваш друг. Я признаю, что это не часть «ядра», поэтому cabal install hoogle

Теперь вы знаете, как «если вы ищете функцию высшего порядка, она уже есть» ( комментарий ephemient ). Но как найти эту функцию? С помощью hoogle!

$ hoogle "Num a => [a] -> a"
Prelude product :: Num a => [a] -> a
Prelude sum :: Num a => [a] -> a

$ hoogle "[Maybe a] -> [a]"
Data.Maybe catMaybes :: [Maybe a] -> [a]

$ hoogle "Monad m => [m a] -> m [a]"
Prelude sequence :: Monad m => [m a] -> m [a]

$ hoogle "[a] -> [b] -> (a -> b -> c) -> [c]"
Prelude zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

Программист hoogle-google не может сам писать свои программы на бумаге так, как он это делает с помощью компьютера. Но вместе с ним и его машиной нельзя * считаться.

Между прочим, если вам понравился хугл, обязательно загляните в hlint!

28
ответ дан 27 November 2019 в 19:38
поделиться

Equational Reasoning

Haskell, будучи чисто функциональным, позволяет вам читать знак равенства как действительный знак равенства (при отсутствии неперекрывающихся шаблонов).

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

Хороший пример такой формы рассуждений можно найти здесь:

http : //www.haskell.org/pipermail/haskell-cafe/2009-March/058603.html

Это также хорошо проявляется в форме законов или прагм RULES, ожидаемых для действительных членов экземпляра, например, Monad законы:

  1. returnrn a >> = f == fa
  2. m >> = return == m
  3. (m >> = f) >> = g == m >>
8
ответ дан 27 November 2019 в 19:38
поделиться

Свободные теоремы

Фил Вадлер познакомил нас с понятием свободной теоремы , и с тех пор мы злоупотребляем ими в Haskell.

Эти замечательные артефакты систем типов в стиле Хиндли-Милнера помогают с уравнениями, используя параметричность, чтобы рассказать вам о том, чего функция не будет делать .

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

  1. forall f g. fmap f. fmap g = fmap (f. g)
  2. fmap id = id

Но свободная теорема говорит нам, что нам не нужно беспокоиться о доказательстве первой, но учитывая вторую, она приходит «бесплатно» только из сигнатуры типа !

fmap :: Functor f => (a -> b) -> f a -> f b

С ленью нужно быть немного осторожнее, но это частично освещено в исходной статье и в книге Яниса Фойгтлендера '

22
ответ дан 27 November 2019 в 19:38
поделиться

Понимание параллельного списка

(Специальная функция GHC)

  fibs = 0 : 1 : [ a + b | a <- fibs | b <- tail fibs ]
6
ответ дан 27 November 2019 в 19:38
поделиться

Лень

Повсеместная лень означает, что вы можете делать такие вещи, как определение

fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

, но она также дает нам множество более тонких преимуществ с точки зрения синтаксиса и рассуждений.

Например, из-за строгости ML должен иметь дело с ограничением значения и очень осторожно отслеживает циклические привязки let, но в Haskell мы можем позволить каждому let быть рекурсивным и не иметь необходимости различать val и fun . Это устраняет серьезную синтаксическую бородавку в языке.

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

Мы можем заменить (почти) все те функции стиля ML, которым требуется take () и возвращать значение, просто с помощью ленивого вычисления значения. Есть причины избегать делать это время от времени, чтобы избежать утечки места с CAF , но такие случаи редки.

Наконец, это позволяет неограниченное сокращение эта-редукции ( \ x -> fx можно заменить на f). Это делает комбинаторно-ориентированное программирование для таких вещей, как комбинаторы синтаксического анализатора, намного более приятным, чем работа с аналогичными конструкциями на строгом языке.

Это помогает вам при рассуждении о программах в бесточечном стиле или о переписывании их в бесточечный стиль и сокращает шум аргумента.

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

Наконец, это позволяет неограниченное сокращение эта-редукции ( \ x -> fx можно заменить на f). Это делает комбинаторно-ориентированное программирование для таких вещей, как комбинаторы синтаксического анализатора, намного более приятным, чем работа с аналогичными конструкциями на строгом языке.

Это помогает вам при рассуждении о программах в бесточечном стиле или о переписывании их в бесточечный стиль и сокращает шум аргумента.

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

Наконец, это позволяет неограниченное сокращение эта-редукции ( \ x -> fx можно заменить на f). Это делает комбинаторно-ориентированное программирование для таких вещей, как комбинаторы синтаксического анализатора, намного более приятным, чем работа с аналогичными конструкциями на строгом языке.

Это помогает вам при рассуждении о программах в бесточечном стиле или о переписывании их в бесточечный стиль и сокращает шум аргумента.

6
ответ дан 27 November 2019 в 19:38
поделиться

Enhanced pattern matching

  • Lazy patterns
  • Irrefutable patterns

    let ~(Just x) = someExpression
    

See pattern matching

6
ответ дан 27 November 2019 в 19:38
поделиться

Monads

They are not that hidden, but they are simply everywhere, even where you don't think of them (Lists, Maybe-Types) ...

3
ответ дан 27 November 2019 в 19:38
поделиться

пусть 5 = ​​6 дюймов ... является допустимым Haskell.

11
ответ дан 27 November 2019 в 19:38
поделиться

Перечисления в стиле C

Объединение сопоставления шаблонов верхнего уровня и арифметических последовательностей дает нам удобный способ определения последовательных значений:

foo : bar : baz : _ = [100 ..]    -- foo = 100, bar = 101, baz = 102
15
ответ дан 27 November 2019 в 19:38
поделиться

Перечисления

Любой тип, который является экземпляром Enum , может использоваться в арифметической последовательности, а не только числа:

alphabet :: String
alphabet = ['A' .. 'Z']

Включая ваши собственные типы данных, просто производите из Enum, чтобы получить реализацию по умолчанию:

data MyEnum = A | B | C deriving(Eq, Show, Enum)

main = do
    print $ [A ..]                 -- prints "[A,B,C]"
    print $ map fromEnum [A ..]    -- prints "[0,1,2]"
5
ответ дан 27 November 2019 в 19:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: