Оператор Haskell to-infix в F #
Существует обычная проблема: F # не поддерживает в стиле infix-стиле функции, доступные в Haskell:
isInfixOf :: Eq a => [a] -> [a] -> Bool
isInfixOf "bar" "foobarbaz"
"bar" `isInfixOf` "foobarbaz"
Наиболее известное решение для F # можно найти здесь:
let isInfixOf (what:string) (where:string) =
where.IndexOf(what, StringComparison.OrdinalIgnoreCase) >= 0
let found = "bar" |>isInfixOf<| "foobarbaz"
Кроме того, его легко немного улучшить, используя приоритет собственных операторов:
let ($) = (|>)
let (&) = (<|)
let found = "bar" $isInfixOf& "foobarbaz"
Также существует XML-ish </style/>
, описанный здесь.
Я хотел бы найти лучшее решение со следующими критериями:
- Оператор одиночного символа (или пара), который не уничтожает обычно используемые операторы;
- В Haskell должен присутствовать один и тот же символ, так же как и символ серьезного акцента (обратной цитаты);
-
Он не должен уничтожать ассоциативность (цепочка поддержки):
let found = "barZZZ" |>truncateAt<| 3 |>isInfixOf<| "foobarbaz"
-
Необязательно, он должен поддерживать функции, берущие кортежи:
let isInfixOf (what:string, where:string) = ...
// it will not work with |> and <|
-
Необязательно, он должен изящно обрабатывать функции /3:
val f: 'a -> 'b -> 'c -> 'd = ...
let curried = a |>f<| b c
// this wouldn't compile as the compiler would attempt to apply b(c) first
P.S. Различные трюки для кодирования также приветствуются, поскольку я считаю, что хороший (когда проверяется командой F # Dev) может быть частью языка в будущем.
Ответы
Ответ 1
Я согласен с тем, что способность превращать функции в операторы infix в Haskell в некоторых ситуациях является опрятной. Тем не менее, я не уверен, что эта функция хорошо впишется в обычный стиль программирования F #, так как это может быть достигнуто с помощью членов.
Например, давайте возьмем ваш фрагмент, который использует truncateAt
и isInfixOf
:
let found = "barZZZ" |>truncateAt<| 3 |>isInfixOf<| "foobarbaz"
Если мы определяем truncateAt
и isInfixOf
как методы расширения string
, вы можете написать:
let found = "barrZZZ".TruncateAt(3).IsInfixOf("foobarbaz")
Эта версия короче, и я лично считаю, что она также более читаема (особенно для тех, кто использует фон программирования .NET в отличие от фона Haskell). Вы также получаете IntelliSense, когда вы нажимаете .
, что является хорошим бонусом. Конечно, вы должны определить эти операции как методы расширения, поэтому вам нужно более тщательно рассмотреть дизайн ваших библиотек.
Для полноты методы расширения определяются следующим образом:
type System.String with
member what.IsInfixOf(where:string) =
where.IndexOf(what, StringComparison.OrdinalIgnoreCase) >= 0
member x.TruncateAt(n) =
x.Substring(0, n)
Ответ 2
let (|<) f x = f x
let f = sprintf "%s%s"
let x = "A" |> f |< "B" |> f |< "C" //"ABC"