Я некоторое время пытался вникнуть в F#, но все время откладывал. Почему?
Потому что независимо от того, на какой ресурс «для начинающих» я пытаюсь заглянуть, я вижу очень простые примеры, которые начинают использовать оператор ->.
Однако нигде я еще не нашел, что дает четкое простое объяснение того, что означает этот оператор. Как будто это должно быть настолько очевидно, что не требует объяснений даже для полных новичков.
Поэтому я должен быть очень тупым, или, возможно, меня сдерживает почти 3 десятилетия предыдущего опыта.
Может ли кто-нибудь объяснить это или указать на действительно доступный ресурс, который это объясняет?
На самом деле Мафусаил прожил 969 лет, так что я все равно был бы на треть его возраста. Впрочем, отредактировал спасибо;)
Если бы использованная единица возраста была на самом деле лунным месяцем, мы бы увидели, что Мафусаил умирает в зрелом возрасте 75 лет, что вполне достоверно и, безусловно, заслуживает внимания для кочевников в пустыне.





Это в основном означает «соответствует». Прочтите это так или как «трансформируется в» или что-то в этом роде.
Итак, из учебника F# за 20 минут,
> List.map (fun x -> x % 2 = 0) [1 .. 10];;
val it : bool list
= [false; true; false; true; false; true; false; true; false; true]
The code (fun i -> i % 2 = 0) defines an anonymous function, called a lambda expression, that has a parameter x and the function returns the result of "x % 2 = 0", which is whether or not x is even.
Также можно использовать эту ссылку: research.microsoft.com/fsharp/manual/lexyacc.aspx
От Microsoft:
Function types are the types given to first-class function values and are written int -> int. They are similar to .NET delegate types, except they aren't given names. All F# function identifiers can be used as first-class function values, and anonymous function values can be created using the (fun ... -> ...) expression form.
(a -> b) означает «функция от a до b». В аннотации типа он обозначает тип функции. Например, f: (int -> String) означает, что f относится к функции, которая принимает целое число и возвращает строку. Он также используется как конструктор таких значений, как в
val f : (int -> int) = fun n -> n * 2
который создает значение, которое является функцией от некоторого числа n до того же числа, умноженного на два.
Хорошая вещь в таких языках, как Haskell (он очень похож на F#, но я не знаю точного синтаксиса - это должно помочь вам понять ->, хотя), что вы можете применять только части аргумента для создания карри функции:
adder n x y = n + x + y
Другими словами: «дайте мне три вещи, и я сложу их вместе». Когда вы вводите в него числа, компилятор выводит типы n x и y. Скажите, что вы пишете
adder 1 2 3
Типы 1, 2 и 3 - Int. Следовательно:
adder :: Int -> Int -> Int -> Int
То есть дайте мне три целых числа, и я в конце концов стану целым числом, или то же самое, что сказать:
five :: Int
five = 5
Но вот что самое интересное! Попробуй это:
add5 = adder 5
Как вы помните, сумматор принимает int, int, int и возвращает вам int. Однако это не вся правда, как вы вскоре увидите. Фактически, add5 будет иметь такой тип:
add5 :: Int -> Int -> Int
Это будет похоже на то, как если бы вы «отклеили» целые числа (крайнее левое) и приклеили их непосредственно к функции. Присмотревшись к сигнатуре функции, мы заметим, что символы -> являются правоассоциативными, то есть:
addder :: Int -> (Int -> (Int -> Int))
Это должно прояснить: когда вы даете сумматору первое целое число, он будет оценивать то, что находится справа от первой стрелки, или:
add5andtwomore :: Int -> (Int -> Int)
add5andtwomore = adder 5
Теперь вы можете использовать add5andtwomore вместо "adder 5". Таким образом, вы можете применить другое целое число, чтобы получить (скажем) "add5and7andonemore":
add5and7andonemore :: Int -> Int
add5and7andonemore = adder 5 7
Как видите, add5and7andonemore хочет еще один аргумент, и когда вы дадите ему один, он внезапно станет целым числом!
> add5and7andonemore 9
=> ((add5andtwomore) 7) 9
=> ((adder 5) 7) 9)
<=> adder 5 7 9
Подставляя параметры в сумматор (n x y) для (5 7 9), получаем:
> adder 5 7 9 = 5 + 7 + 9
=> 5 + 7 + 9
=> 21
Фактически, plus также является просто функцией, которая принимает int и возвращает вам другой int, поэтому приведенное выше действительно больше похоже:
> 5 + 7 + 9
=> (+ 5 (+ 7 9))
=> (+ 5 16)
=> 21
Вот так!
Здесь уже есть много отличных ответов, я просто хочу добавить к разговору еще один способ думать об этом.
'->' означает функцию.
'a ->' b - это функция, которая принимает 'a и возвращает' b
('a *' b) -> ('c *' d) - это функция, которая принимает кортеж типа ('a,' b) и возвращает кортеж ('c,' d). Например, int / string возвращает float / char.
Что интересно, так это в каскадном случае 'a ->' b -> 'c. Это функция, которая принимает 'a и возвращает функцию (' b -> 'c), или функция, которая принимает' b -> 'c.
Итак, если вы напишете: пусть f x y z = ()
Типом будет f: 'a ->' b -> 'c -> unit, поэтому, если вы применили только первый параметр, результатом была бы каррированная функция' b -> 'c ->' unit.
В контексте определения функции он похож на => из лямбда-выражения в C# 3.0.
F#: let f = fun x -> x*x
C#: Func<int, int> f = x => x * x;
-> в F# также используется в сопоставлении с образцом, где это означает: если выражение соответствует части между | и ->, то то, что идет после ->, должно быть возвращено в качестве результата:
let isOne x = match x with
| 1 -> true
| _ -> false
Первый вопрос: знакомы ли вы с лямбда-выражениями в C#? Если это так, -> в F# то же самое, что => в C# (я думаю, вы читаете, что он «идет к»).
Оператор -> также можно найти в контексте сопоставления с образцом
match x with
| 1 -> dosomething
| _ -> dosomethingelse
Я не уверен, что это тоже лямбда-выражение или что-то еще, но я думаю, что «идет к» все еще сохраняется.
Возможно, вы действительно имеете в виду «загадочные» ответы парсера F#:
> let add a b = a + b
val add: int -> int -> int
Это означает (как объясняется в большинстве примеров), что add - это 'val', которое принимает два целых числа и возвращает целое число. Для меня это было совершенно непонятно с самого начала. Я имею в виду, как мне узнать, что add - это не val, которое принимает один int и возвращает два int?
Что ж, дело в том, что в каком-то смысле это так. Если я добавлю всего один int, я верну (int -> int):
> let inc = add 1
val inc: int -> int
Это (каррирование) - одна из тех вещей, которые делают F# таким сексуальным для меня.
Чтобы получить полезную информацию о F#, я обнаружил, что блоги НАМНОГО полезнее, чем любая официальная «документация»: вот несколько имен, которые стоит проверить.
'->' не является оператором. Он встречается в синтаксисе F# в нескольких местах, и его значение зависит от того, как он используется как часть более крупной конструкции.
Внутри типа '->' описывает типы функций, как описано выше. Например
let f : int -> int = ...
говорит, что 'f' - это функция, которая принимает int и возвращает int.
Внутри лямбды («вещь, которая начинается с ключевого слова 'fun'») '->' - это синтаксис, отделяющий аргументы от тела. Например
fun x y -> x + y + 1
- выражение, определяющее функцию с двумя аргументами с заданной реализацией.
Внутри конструкции "match" '->' - это синтаксис, который отделяет шаблоны от кода, который должен выполняться, если шаблон совпадает. Например, в
match someList with
| [] -> 0
| h::t -> 1
материал слева от каждого '->' - это шаблоны, а материал справа - то, что происходит, если шаблон слева был сопоставлен.
Сложность понимания может быть связана с ошибочным предположением, что '->' является «оператором» с одним значением. Аналогия может быть "." в C#, если вы никогда раньше не видели никакого кода, и попробуйте проанализировать "." Оператор, основанный на просмотре "obj.Method", "3.14" и "System.Collections", может сильно запутать, потому что этот символ имеет разное значение в разных контекстах. Однако, когда вы знаете язык достаточно, чтобы распознавать эти контексты, все становится ясно.
Я принял это как ответ, потому что это шкаф для того полного ответа, который я искал, но есть ряд других деталей из других ответов, которые помогают. Я не понимаю, что -> не является оператором. Кто-нибудь еще может это подтвердить?
Можно сказать, что это оператор уровня типа. Скорее конструктор типа данных. (a -> b) изоморфно (a, b). Стрелка в первом случае аналогична запятой во втором.
Апокалипс, потребовалось несколько чтений (вы предполагаете, что я знал, что изоморфные средства сдерживали меня), но теперь я понял.
А теперь в шлейфе for это означает do yield ([for i in 0..n -> i]).
Много отличных ответов на этот вопрос, спасибо людям. Я хотел бы дать здесь редактируемый ответ, который сводит все воедино.
Для тех, кто знаком с пониманием C# -> то же самое, что и => выражение lamba, - хороший первый шаг. Это использование: -
fun x y -> x + y + 1
Может пониматься как эквивалент: -
(x, y) => x + y + 1;
Однако ясно, что -> имеет более фундаментальное значение, которое проистекает из концепции, что функция, которая принимает два параметра, такие как выше, может быть сокращена (это правильный термин?) До серии функций, принимающих только один параметр.
Следовательно, когда выше описывается так: -
Int -> Int -> Int
Это действительно помогло узнать, что -> правильно ассоциативно, поэтому можно рассмотреть вышесказанное: -
Int -> (Int -> Int)
Ага! У нас есть функция, которая принимает Int и возвращает (Int -> Int) (каррированная функция?).
Объяснение, что -> также может появляться как часть определения типа, также помогло. (Int -> Int) - это тип любой функции, которая принимает Int и возвращает Int.
Также полезно то, что -> появляется в другом синтаксисе, таком как сопоставление, но там он не имеет того же значения? Это верно? Я не уверен, что это так. Я подозреваю, что оно имеет то же значение, но у меня пока нет слов, чтобы это выразить.
Обратите внимание, что цель этого ответа не в том, чтобы дать дополнительные ответы, а в том, чтобы вы могли совместно редактировать его, чтобы создать более окончательный ответ. В общем, было бы хорошо, если бы все неясности и пустяки (такие как этот параграф) были бы удалены и добавлены лучшие примеры. Попробуем сделать этот ответ максимально доступным для непосвященных.
Я вернусь, когда наберусь репутации :)
Итак, в определении типа - Int -> Int -> Int = функция, которая принимает int и возвращает функцию, которая принимает int и возвращает int - это правильно?
Мне тоже все это кажется греческим.