Использование `inline` в F # - PullRequest
       35

Использование `inline` в F #

63 голосов
/ 20 сентября 2010

Мне кажется, что ключевое слово inline в F # имеет несколько иную цель, чем то, к чему я привык, например, в. C. Например, кажется, что это влияет на тип функции (что такое «статически разрешаемые параметры типа»? Разве не все типы F # разрешаются статически?)

Когда мне следует использовать inline функции?

Ответы [ 4 ]

76 голосов
/ 20 сентября 2010

Ключевое слово inline указывает, что определение функции должно быть встроено в любой код, который его использует.В большинстве случаев это не повлияет на тип функции.Однако в редких случаях это может привести к функции, которая имеет более общий тип, поскольку существуют ограничения, которые не могут быть выражены в скомпилированном виде кода в .NET, но которые могут быть применены, когда функция встроена.

Основным случаем, в котором это применимо, является использование операторов.

let add a b = a + b

будет иметь мономорфный логический тип (вероятно, int -> int -> int, но это может быть что-то вроде float -> float -> float, если выесть код, который использует эту функцию вместо этого типа).Однако, пометив эту функцию как встроенную, компилятор F # выведет полиморфный тип:

let inline add a b = a + b
// add has type ^a ->  ^b ->  ^c when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)

Нет способа кодировать это ограничение типа первоклассным способом в скомпилированном коде в .NET.Однако компилятор F # может применить это ограничение на сайте, где он указывает функцию, так что все операции оператора разрешаются во время компиляции.

Параметры типа ^a, ^b и ^cявляются «статически разрешаемыми параметрами типа», что означает, что типы аргументов должны быть статически известны на сайте, где используются эти параметры.Это отличается от параметров нормального типа (например, 'a, 'b и т. Д.), Где параметры означают что-то вроде «некоторого типа, который будет предоставлен позже, но который может быть любым».

29 голосов
/ 21 сентября 2010

Когда мне следует использовать inline функции?

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

Например, inline в следующей функции fold делает его в 5 раз быстрее:

  let inline fold f a (xs: _ []) =
     let mutable a = a
     for i=0 to xs.Length-1 do
        a <- f a xs.[i]
     a

Обратите внимание, что это мало похоже на то, что inline делает в большинстве других языков.Вы можете добиться аналогичного эффекта, используя метапрограммирование шаблонов в C ++, но F # также может быть встроенным между скомпилированными сборками, потому что inline передается через метаданные .NET.

25 голосов
/ 15 февраля 2015

Вы должны использовать inline, когда вам нужно определить функцию, для которой ее тип (пере) должен оцениваться на сайте каждого использования, в отличие от обычной функции, у которой ее тип будет оцениваться (выводиться) только на сайте.первого использования, а затем считаются статически типизированными с этой первой предполагаемой сигнатурой типа везде и после этого.

В встроенном случае определение функции является по сути универсальным / полиморфным, тогда как в обычном (не встроенном)) в этом случае функция статически (и часто неявно) типизирована.

Итак, если вы используете inline, следующий код:

let inline add a b = a + b

[<EntryPoint>]
let main args = 

    let one = 1
    let two = 2
    let three = add one two
    // here add has been compiled to take 2 ints and return an int

    let dog = "dog"
    let cat = "cat"
    let dogcat = add dog cat
    // here add has been compiled to take 2 strings and return a string

    printfn "%i" three
    printfn "%s" dogcat   

    0

будет скомпилирован и скомпилирован для получения следующего вывода:

3  
dogcat

Другими словами, одно и то же определение функции добавления использовалось для создания как функции, которая добавляет к целым числам, так и функции, которая объединяет две строки (фактически, перегрузка базового оператора на + такжедостигается под капотом с помощью встроенного).

В то время как этот код, идентичный, за исключениемчто функция добавления больше не объявляется как встроенная:

let add a b = a + b

[<EntryPoint>]
let main args = 

    let one = 1
    let two = 2
    let three = add one two
    // here add has been compiled to take 2 ints and return an int

    let dog = "dog"
    let cat = "cat"
    let dogcat = add dog cat
    // since add was not declared inline, it cannot be recompiled
    // and so we now have a type mismatch here

    printfn "%i" three
    printfn "%s" dogcat   

    0

не будет компилироваться, что приведет к неудаче с этой жалобой:

    let dogcat = add dog cat
                     ^^^ - This expression was expected to have type int
                           but instead has type string

Хороший пример того, как целесообразно использовать inline, - это когда выхотите определить обобщенную функцию, чтобы изменить порядок применения аргументов функции с 2 аргументами, например,

let inline flip f x y = f y x

, как это сделано в ответе @pad на этот вопрос Другой порядок аргументов для получения N-го элемента Array, List или Seq .

7 голосов
/ 21 сентября 2010

В руководстве по проектированию F # об этом упоминается лишь немного. Моя рекомендация (которая хорошо согласуется с тем, что там сказано):

  • Не использовать inline
    • Исключение: вы можете рассмотреть возможность использования inline при написании математических библиотек для использования другим кодом F #, и вы хотите написать функции, которые являются общими для разных числовых типов данных.

Существует множество других «интересных» способов использования встроенных и статических ограничений членов для сценариев типа «утка», которые работают немного как шаблоны C ++. Мой совет - избегать всего этого, как чумы.

@ kvb отвечает более подробно о том, что такое «ограничения статического типа».

...