Разница между двумя функциями, создающими одноэлементный список
При запуске hlint над моей программой сообщалось об ошибке для
\x -> [x]
и предложил альтернативную форму
(: [])
Что там ошибочно согласно подсказке о первой форме, и, следовательно, почему я должен использовать (менее читаемый) второй вариант?
редактировать
(явно добавил подсказку к вопросу)
Мой вопрос не столько в том, в чем разница (я понимаю их обоих) с лексической точки зрения. Моя проблема в том, что я не понимаю, почему hlint помечает это как ошибку. Есть ли, например, разница в лени? Кроме того, почему предыдущая мысль ошибочной \x -> Just x
вызывает только предупреждение.
3 ответа
Обычный вопрос, на который я только что добавил ответ в руководстве по HLint. Это говорит:
Каждый намек имеет уровень серьезности:
- Ошибка - например
concat (map f x)
предполагаетconcatMap f x
как подсказка серьезности "ошибки". С точки зрения стиля, вы всегда должны заменить комбинациюconcat
а такжеmap
сconcatMap
, Обратите внимание, что оба выражения эквивалентны - HLint сообщает об ошибке стиля, а не фактической ошибке в коде.- Предупреждение - например
x !! 0
предполагаетhead x
в качестве "предупреждения" указывается серьезность. типичноhead
это более простой способ выражения первого элемента списка, особенно если вы относитесь к списку индуктивно. Однако в выраженииf (x !! 4) (x !! 0) (x !! 7)
замена среднего аргумента на голову усложняет следование шаблону и, вероятно, является плохой идеей. Предупреждающие подсказки часто имеют смысл, но их не следует применять вслепую.Разница между ошибкой и предупреждением - это личный вкус, обычно мой личный вкус. Если у вас уже есть хорошо развитое чувство стиля Haskell, вы должны игнорировать разницу. Если вы начинающий программист на Haskell, вы можете сосредоточиться на подсказках об ошибках, прежде чем предупреждать о них.
Хотя разница в личном вкусе, иногда я передумал. Глядя на два примера в этой теме, (:[])
кажется довольно "сложным" намеком - вы ломаете синтаксический сахар [x]
в x:[]
, который в некотором роде очищает абстракцию списка как универсального контейнера, если вы никогда не сопоставляете шаблон с ним. По сравнению \x -> Just x
в Just
всегда кажется хорошей идеей. Поэтому в HLint-1.8.43 (только что выпущенном) я сделал первое предупреждение, а второе ошибку.
Там нет никакой разницы. HLint занимается вопросами стиля; в конечном счете, это всего лишь подсказки о том, как сделать ваш код лучше.
В общем, использование лямбды с конструктором или подобной функцией является избыточным и затрудняет чтение кода. В качестве крайнего примера возьмем конструктор, как Just
как пример: сравнить Just
в \ x -> Just x
, Это эквивалентно, но вторая версия, безусловно, делает вещи более запутанными! В качестве более близкого примера, большинство людей выбрали бы (+ 1)
над \ x -> x + 1
,
В вашем конкретном случае это другая история, потому что списки имеют специальный синтаксис. Так что если вам нравится \ x -> [x]
Версия лучше, просто держи ее. Однако, как только вы привыкнете к разделам оператора, скорее всего, вы найдете (: [])
версию так же легко - если не проще - читать, так что рассмотрите возможность ее использования даже сейчас.
Я мог бы рассмотреть возможность использования return
или же pure
за это:
ghci> return 0 :: [Int]
[0]
ghci> import Control.Applicative
ghci> pure 0 :: [Int]
[0]
Мне нужно было включить аннотацию типа (:: [Int]
) потому что я работал в GHCi. В середине другого кода вам, вероятно, это не понадобится.