Разница между двумя функциями, создающими единый список

При запуске hlint над моей программой сообщалось ошибка для

\x -> [x]

и предложил альтернативную форму

(: [])

Что существует ошибочно в соответствии с hlint о первой форме и, следовательно, почему я должен использовать (менее читаемый) второй вариант?

Изменить

(добавлено hlint явно на вопрос)

Мой вопрос кроется не столько в том, какова разница (я понимаю их обоих) в лексической точке зрения. Моя проблема заключается в том, что я не понимаю, почему hlint отмечает это как ошибку. Есть ли, например, разница в лень? Кроме того, почему предыдущая мысль считается ошибочной по hlint, а \x -> Just x вызывает только предупреждение.

Ответ 1

Общий вопрос, на который я только что добавил ответ в Руководство 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, вы можете сосредоточиться на подсказках ошибок до предупреждений.

В то время как разница - личный вкус, иногда я перехожу на ум. Рассматривая два примера в этом потоке, (:[]) кажется относительно "сложным" намеком - вы разрушаете синтаксический сахар от [x] до x:[], который каким-то образом просматривает абстракцию списка как общий контейнер, если вы никогда не сопоставляете ему шаблон. Напротив, \x -> Just x to Just всегда кажется хорошей идеей. Поэтому в HLint-1.8.43 (только что выпущен) я сделал первое предупреждение, а второе - ошибкой.

Ответ 2

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

В общем случае использование лямбда с конструктором или функцией вроде этого избыточно и делает код более трудным для чтения. В качестве крайнего примера возьмите конструктор типа Just в качестве примера: сравните Just с \ x -> Just x. Они эквивалентны, но вторая версия, безусловно, делает вещи более запутанными! В качестве более близкого примера большинство людей выбрали бы (+ 1) более \ x -> x + 1.

В вашем конкретном случае это другая история, потому что списки имеют специальный синтаксис. Поэтому, если вам нравится версия \ x -> [x] лучше, просто сохраните ее. Однако, как только вы привыкнете к разделам оператора, вероятно, вы найдете версию (: []) такой же простой, если не простой, для чтения, поэтому подумайте об использовании ее даже сейчас.

Ответ 3

Я мог бы использовать return или pure для этого:

ghci> return 0 :: [Int]
[0]

ghci> import Control.Applicative
ghci> pure 0 :: [Int]
[0]

Мне нужно было включить аннотацию типа (:: [Int]), потому что я работал в GHCi. В середине кучи другого кода вам, вероятно, не понадобится.