Склад Haskell (.) Проти оператора передачі труб F # (|>)


100

У F # використання оператора трубопровідного руху |>досить поширене. Однак у Haskell я лише коли-небудь бачив склад функції (.), який використовується. Я розумію, що вони пов'язані між собою , але чи є мовна причина того, що в Haskell не використовується pipe-forward або це щось інше?


2
Відповідь lihanseys говорить, що &це Хаскелл |>. Заглибився глибоко в цю нитку і знадобився мені кілька днів, щоб відкрити. Я його дуже використовую, тому що ви, природно, читаєте зліва направо, щоб слідувати коду.
itmuckel

Відповіді:


61

Я трохи роздумую ...

Культура : Я думаю |>, що це важливий оператор у F # "культурі", а можливо, аналогічно з .Haskell. F # має оператор композиції функцій, <<але я думаю, що спільнота F # має тенденцію використовувати стиль без точок менше, ніж у спільноти Haskell.

Мовні відмінності : я не знаю достатньо обох мов для порівняння, але, можливо, правила узагальнення прив’язок є досить різними, щоб вплинути на це. Наприклад, я знаю, що в F # іноді пишу

let f = exp

не компілюється, і вам потрібна явна ета-конверсія:

let f x = (exp) x   // or x |> exp

змусити його складати. Це також відволікає людей від стилю, що не належить до очок / композиції, та до конвеєрного стилю. Також висновок типу F # іноді вимагає конвеєрного руху, так що відомий тип з’являється зліва (див. Тут ).

(Особисто я вважаю, що стиль без очок не читається, але я вважаю, що кожна нова / інша річ здається нечитабельною, поки ти не звикнеш до неї.)

Я думаю, що обидва є потенційно життєздатними на будь-якій мові, і історія / культура / нещасний випадок може визначити, чому кожна громада оселилася на різному «притягувачі».


7
Я згоден з культурними відмінностями. Традиційний Haskell використовує .і $, тому люди продовжують їх використовувати.
Амок

9
Точка вільна іноді більше читається, ніж точкова, іноді менше. Я, як правило, використовую його в аргументі для функцій, таких як карта і фільтр, щоб уникнути того, що лямбда забиває речі. Я іноді використовую його і у функціях вищого рівня, але рідше і лише тоді, коли це щось прямо.
Пол Джонсон

2
Я не бачу в цьому багато культури, в тому сенсі, що просто немає великого вибору в цьому питанні, що стосується F # (з причин, які ви та Ганеш згадуєте). Тому я б сказав, що обидва життєздатні в Haskell, але F #, безумовно, набагато краще оснащений для використання оператора трубопроводу.
Курт Шельфтаут

2
так, культуру читання зліва направо :) навіть математику слід вчити саме так ...
nicolas

1
@nicolas зліва направо - це довільний вибір. Ви можете звикнути справа наліво.
Мартін Каподічі

86

У F # (|>)важливо через перевірку типу "зліва направо". Наприклад:

List.map (fun x -> x.Value) xs

як правило, не буде перевірено набір тексту, оскільки навіть якщо тип xsвідомий, тип аргументу xлямбда не відомий у той момент, коли його перевіряє, тому він не знає, як вирішити x.Value.

У контрасті

xs |> List.map (fun x -> x.Value)

буде добре працювати, тому що тип xsводіння призведе до типу xвідомого.

Перевірка типу вліво-вправо потрібна через роздільну здатність імен, що беруть участь у таких конструкціях x.Value. Саймон Пейтон Джонс написав пропозицію щодо додавання подібного виду дозволу імені до Haskell, але він пропонує використовувати локальні обмеження, щоб відстежувати, підтримує якийсь тип певну операцію чи ні. Тож у першому зразку вимога, яка xпотребує Valueвластивості, переноситиметься до тих пір, поки xsне буде видно, і ця вимога може бути вирішена. Це, однак, ускладнює систему типів.


1
Цікаво, що в Haskell є (<|) оператор, схожий на (.) З тим самим напрямком даних справа наліво. Але як працюватиме роздільна здатність для нього?
The_Ghost

7
(<|) насправді схожий на Haskell ($). Перевірка типу вліво-вправо потрібна лише для вирішення таких речей, як .Value, тому (<|) добре працює в інших сценаріях або якщо ви використовуєте явні анотації типу.
GS - Вибачте Моніку

44

Більше спекуляцій, цього разу з боку переважно Хаскелл ...

($)це фліп (|>), і його використання є досить поширеним, коли ви не можете написати безточний код. Отже, головна причина, що (|>)не використовується в Haskell, полягає в тому, що її місце вже зайнято ($).

Крім того, кажучи з трохи досвіду F #, я думаю (|>), що такий популярний у F # коді, оскільки він нагадує Subject.Verb(Object)структуру ОО. Оскільки F # має на меті плавну функціональну / інтеграцію OO, Subject |> Verb Objectце досить плавний перехід для нових функціональних програмістів.

Особисто мені теж подобається думати зліва направо, тому я використовую (|>)в Haskell, але я не думаю, що так багато інших людей.


Вітаю, Натане, наперед визначено "flip ($)" в будь-якій точці платформи Haskell? Ім'я "(|>)" вже визначене в Data.Sequence з іншим значенням. Якщо ще не визначено, як ви це називаєте? Я думаю про те, щоб піти з "($>) = flip ($)"
mattbh

2
@mattbh: Не те, що я можу знайти в Google. Я не знав про це Data.Sequence.|>, але $>здається розумним уникати конфліктів там. Чесно кажучи, є лише стільки гарних операторів, тому я просто використовую |>для обох і керую конфлікти в кожному конкретному випадку. (Крім того, я був би спокуса просто псевдонім , Data.Sequence.|>як snoc)
Натан Шівелі-Сандерс

2
($)і (|>)є застосуванням не складом. Два пов'язані (як питання примітки) , але вони не збігаються (ваше fcє (Control.Arrow.>>>)для функцій).
Натан Шивелі-Сандерс

11
На практиці, F # |>насправді більше нагадує мені UNIX, |ніж будь-що інше.
Кевін Канту

3
Ще однією перевагою |>F # є те, що він має приємні властивості для IntelliSense у Visual Studio. Введіть |>і ви отримаєте список функцій, які можна застосувати до значення зліва, подібного до того, що відбувається під час введення тексту .після об’єкта.
Крістофер Джонсон

32

Я думаю, що ми плутаємо речі. Haskell's ( .) еквівалентний F # 's ( >>). Не плутати з F # 's ( |>), який є просто перевернутим додатком функції і схожий на Haskell ( $) - зворотний:

let (>>) f g x = g (f x)
let (|>) x f = f x

Я вважаю, що програмісти Haskell $часто використовують часто. Можливо, не так часто, як зазвичай використовують програмісти F # |>. З іншого боку, деякі хлопці F # >>до смішного рівня використовують: http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx


2
як ви говорите, як в Haskell $оператор - звернуто, ви можете легко визначити його як: a |> b = flip ($)яке стає еквівалентно F # 's трубопроводу , наприклад , ви можете зробити[1..10] |> map f
візаві

8
Я думаю ( .) те саме, що ( <<), тоді як ( >>) - зворотний склад. Це ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3проти( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
Dobes Vandermeer

-1 за "використання >> до смішної міри". (Ну, я не, але ви зрозуміли мою думку). F в F # - це "функціональний", тому склад функції є законним.
Флоріан F

1
Звичайно, я згоден, що склад функції є законним. Під "деякими F # хлопцями" я маю на увазі себе! Це мій власний блог. : P
AshleyF

Я не думаю, що .це рівнозначно >>. Я не знаю, чи є F #, <<але це було б еквівалентом (як у Elm).
Метт Столяр

28

Якщо ви хочете використовувати F # 's |>в Haskell, то в Data.Function є &оператором (з тих пір base 4.8.0.0).


Будь-яка причина для Haskell вибрати &більш |>? Я відчуваю, що |>набагато інтуїтивніше, і це також нагадує мені оператора труб Unix.
yeshengm

16

Композиція зліва направо в Хаскеллі

Деякі користуються стилем зліва направо (передача повідомлень) і в Haskell. Дивіться, наприклад, бібліотеку mps про Hackage. Приклад:

euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum

Я думаю, що цей стиль виглядає приємно в деяких ситуаціях, але це важче читати (потрібно знати бібліотеку та всіх її операторів, переосмислене (.)теж непокоїть).

У Control.Category , що входить до базового пакету, також є оператори композиції зліва направо, а також справа наліво . Порівняйте >>>і <<<відповідно:

ghci> :m + Control.Category
ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
[9,5]

Існує вагома причина, щоб іноді віддавати перевагу композиції зліва направо: оцінка порядку дотримується порядку читання.


Приємно, так що (>>>) багато в чому прирівняний до (|>)?
Ханзор

@Khanzor Не зовсім. (|>) застосовує аргумент, (>>>) - це переважно функціональний склад (або подібні речі). Тоді я припускаю, що є певна різниця у коректності (не перевіряла).
зустріч

15

Я бачив, як >>>його використовують flip (.), і я часто це використовую сам, особливо для довгих ланцюгів, які найкраще розуміють зліва направо.

>>> насправді від Control.Arrow і працює над більш ніж просто функціями.


>>>визначено в Control.Category.
Стівен Шоу

13

Окрім стилю та культури, це зводиться до оптимізації мовного дизайну для чистого чи нечистого коду.

|>Оператор часто зустрічається в F # в основному тому , що це допомагає приховати два обмеження , які з'являються з переважно-нечистим кодом:

  • Вивод зліва направо без структурних підтипів.
  • Значення обмеження.

Зауважимо, що колишнє обмеження не існує в OCaml, оскільки підтипізація є структурною замість номінальної, тому структурний тип легко уточнюється за допомогою уніфікації в міру просування виводу типу.

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


8

Я думаю, що оператор F #'s pipe forward ( |>) повинен проти ( & ) у haskell.

// pipe operator example in haskell

factorial :: (Eq a, Num a) =>  a -> a
factorial x =
  case x of
    1 -> 1
    _ -> x * factorial (x-1)
// terminal
ghic >> 5 & factorial & show

Якщо вам не подобається ( &) оператор, ви можете налаштувати його, як F # або Elixir:

(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 1 |>
ghci>> 5 |> factorial |> show

Чому infixl 1 |>? Див. Документ у функції даних (&)

infixl = інфікс + ліва асоціативність

infixr = інфікс + права асоціативність


(.)

( .) означає функціональний склад. Це означає (fg) (x) = f (g (x)) в математиці.

foo = negate . (*3)
// ouput -3
ghci>> foo 1
// ouput -15
ghci>> foo 5

вона дорівнює

// (1)
foo x = negate (x * 3) 

або

// (2)
foo x = negate $ x * 3 

( $) Оператор також визначається в Data-Function ($) .

( .) використовується для створення Hight Order Functionабо closure in js. Див. Приклад:


// (1) use lamda expression to create a Hight Order Function
ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]  
[-5,-3,-6,-7,-3,-2,-19,-24]


// (2) use . operator to create a Hight Order Function
ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]  
[-5,-3,-6,-7,-3,-2,-19,-24]

Нічого собі, менше (код) краще.


Порівняйте |>і.

ghci> 5 |> factorial |> show

// equals

ghci> (show . factorial) 5 

// equals

ghci> show . factorial $ 5 

Це різниця між left —> rightі right —> left. ⊙﹏⊙ |||

Гуманізація

|>і &краще, ніж.

тому що

ghci> sum (replicate 5 (max 6.7 8.9))

// equals

ghci> 8.9 & max 6.7 & replicate 5 & sum

// equals

ghci> 8.9 |> max 6.7 |> replicate 5 |> sum

// equals

ghci> (sum . replicate 5 . max 6.7) 8.9

// equals

ghci> sum . replicate 5 . max 6.7 $ 8.9

Як функціональне програмування об'єктно-орієнтованою мовою?

будь ласка, відвідай http://reactivex.io/

Він підтримує:

  • Java: RxJava
  • JavaScript: RxJS
  • C #: Rx.NET
  • C # (Єдність): UniRx
  • Scala: RxScala
  • Clojure: RxClojure
  • C ++: RxCpp
  • Луа: RxLua
  • Рубін: Rx.rb
  • Пітон: RxPY
  • Перейти: RxGo
  • Groovy: RxGroovy
  • JRuby: RxJRuby
  • Котлін: RxKotlin
  • Свіфт: RxSwift
  • PHP: RxPHP
  • Еліксир: реактивний
  • Дартс: RxDart

1

Це мій перший день спробувати Haskell (після Rust і F #), і я зміг визначити оператора F # '|>:

(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 0 |>

і, здається, працює:

factorial x =
  case x of
    1 -> 1
    _ -> x * factorial (x-1)

main =     
    5 |> factorial |> print

Надіюсь, що експерт Haskell може дати вам ще краще рішення.


1
Ви також можете визначити операторів інфіксації infix :) x |> f = f x
Джеймі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.