Указатель NULL
- это тот, который указывает на никуда. Когда вы разыскиваете указатель p
, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p
является нулевым указателем, местоположение, хранящееся в p
, является nowhere
, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception
.
В общем, это потому, что что-то не было правильно инициализировано.
Мой мозг просто взорвался
, При попытке скомпилировать этот код:
{-# 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
Симпатичная защита
Prelude
определяет otherwise = True
, делая полное защитное чтение условий очень естественно.
fac n
| n < 1 = 1
| otherwise = n * fac (n-1)
Гибкая спецификация импорта модуля и экспорта
Импорт и экспорт хороши.
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'
при поиске списка или функции высшего порядка это уже там
существует 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
круглые скобки Предотвращения
(.)
и ($)
функции в 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 -}
Читаемая композиция функций
Prelude
определяет (.)
, чтобы быть составом математической функции; то есть, g . f
первый применяется f
, затем применяется g
к результату.
, Если Вы import Control.Arrow
, следующее эквивалентно:
g . f
f >>> g
Control.Arrow
обеспечивает instance Arrow (->)
, и это хорошо для людей, которым не нравится читать функциональное приложение назад.
Списки Бога
, Так как Вы упомянули 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 параметров и списков.
Неподвижность Оператора
можно использовать эти инфикс, 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`
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"
Дополнительное Расположение
можно использовать явные фигурные скобки и точки с запятой вместо пробела (иначе расположение) для разграничивания блоков.
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 могут быть прямо произведены другими программами.
Обобщенные алгебраические типы данных. Вот пример интерпретатора, в котором система типов позволяет охватить все случаи:
{-# 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
комментарии .
мультилинии Nestable{- inside a comment,
{- inside another comment, -}
still commented! -}
Сокращение от общей операции списка
следующее эквивалентны:
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]
Пользовательские управляющие структуры
у 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 :("
Шаблоны в привязках верхнего уровня
five :: Int
Just five = Just 5
a, b, c :: Char
[a,b,c] = "abc"
Как это круто! Спасает вас тот звонок на из Просто
и голову
то и дело.
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!
Equational Reasoning
Haskell, будучи чисто функциональным, позволяет вам читать знак равенства как действительный знак равенства (при отсутствии неперекрывающихся шаблонов).
Это позволяет вам подставлять определения непосредственно в код, а с точки зрения оптимизации дает компилятору большую свободу действий в отношении того, когда что-то происходит.
Хороший пример такой формы рассуждений можно найти здесь:
http : //www.haskell.org/pipermail/haskell-cafe/2009-March/058603.html
Это также хорошо проявляется в форме законов или прагм RULES, ожидаемых для действительных членов экземпляра, например, Monad законы:
Свободные теоремы
Фил Вадлер познакомил нас с понятием свободной теоремы , и с тех пор мы злоупотребляем ими в Haskell.
Эти замечательные артефакты систем типов в стиле Хиндли-Милнера помогают с уравнениями, используя параметричность, чтобы рассказать вам о том, чего функция не будет делать .
Например, есть два закона, которым должен удовлетворять каждый экземпляр Functor :
Но свободная теорема говорит нам, что нам не нужно беспокоиться о доказательстве первой, но учитывая вторую, она приходит «бесплатно» только из сигнатуры типа !
fmap :: Functor f => (a -> b) -> f a -> f b
С ленью нужно быть немного осторожнее, но это частично освещено в исходной статье и в книге Яниса Фойгтлендера '
Понимание параллельного списка
(Специальная функция GHC)
fibs = 0 : 1 : [ a + b | a <- fibs | b <- tail fibs ]
Лень
Повсеместная лень означает, что вы можете делать такие вещи, как определение
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). Это делает комбинаторно-ориентированное программирование для таких вещей, как комбинаторы синтаксического анализатора, намного более приятным, чем работа с аналогичными конструкциями на строгом языке.
Это помогает вам при рассуждении о программах в бесточечном стиле или о переписывании их в бесточечный стиль и сокращает шум аргумента.
Enhanced pattern matching
Irrefutable patterns
let ~(Just x) = someExpression
See pattern matching
Monads
They are not that hidden, but they are simply everywhere, even where you don't think of them (Lists, Maybe-Types) ...
пусть 5 = 6 дюймов ...
является допустимым Haskell.
Перечисления в стиле C
Объединение сопоставления шаблонов верхнего уровня и арифметических последовательностей дает нам удобный способ определения последовательных значений:
foo : bar : baz : _ = [100 ..] -- foo = 100, bar = 101, baz = 102
Перечисления
Любой тип, который является экземпляром 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]"