________fear_of_underscores

среда, 25 июля 2012 г.

Фишки F#

Params в F# (функции с переменным числом параметров)

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

//using params analogue in F#
type SomeBoilerplateType =
    static member Add ([<ParamArray>] x) = x |> (Array.map float) |> Array.sum

SomeBoilerplateType.Add(1, 2)
SomeBoilerplateType.Add(1,2,3,4,5)
SomeBoilerplateType.Add([|for i in 0..10 -> i|])
Как вы думаете, какого типа будет такой метод? :)
Конечно, int array -> int.
Печально, что использовать это можно только в методах классов (как и, например, ad hoc-полиморфизм (секция "Overloaded Methods"), именованные и опциональные аргументы (перейдя по последней ссылке, промотайте вниз :) )).
Кстати, есть ещё кое-какие тонкости.

Индексаторы

Индексаторы являются "официальной" фишкой F#, только называются они не Indexers, как в C#, а Indexed Properties. На MSDN написано про них практически всё, кроме одной детали: вместо того, чтобы именовать свойство, используемое для индексирования, как Item, можно дать ему любое другое имя, но при этом также задать классу атрибут [<DefaultMember("name")>], где Name — имя свойства.
Пример с Item:
//indexers for F#
type Slowpoke() = 
    let hidden = [|for i in 0..10 -> i|]
    let mutable current = 0
    member this.Item 
        with get(_) = 
            let res = hidden.[current]
            current <- (current + 1) % hidden.Length
            System.Threading.Thread.Sleep(1000)
            res

let slowpoke = new Slowpoke()
slowpoke.[666]
slowpoke.[666]
Пример с атрибутом (заодно — другой вариант синтаксиса для свойства без сеттера):
open System.Reflection
[<DefaultMember("AnotherName")>]
type Slowpoke'() = 
    let hidden = [|for i in 0..10 -> i|]
    let mutable current = 0    
    member this.AnotherName(_) = 
        let res = hidden.[current]
        current <- (current + 1) % hidden.Length
        System.Threading.Thread.Sleep(1000)
        res

let slowpoke' = new Slowpoke'()
slowpoke'.[156]
Кстати, на самом деле, для примера с Item компилятор просто сам добавляет этот атрибут:

typeof<Slowpoke>.GetCustomAttributes(false)
> [|System.Reflection.DefaultMemberAttribute
      {MemberName = "Item";
       TypeId = System.Reflection.DefaultMemberAttribute;}; ... |]

Функции с аргументами-типами (Type Functions)

Кстати, о typeof. Это функция. Не не простая, а так называемая Type Function — функция, которая принимает в качестве своих аргументов типы, а не какие-то значения. Такие функции можно объявлять только в модулях, в классах же, типах и выражениях их объявлять нельзя.
Например, мы можем объявить вот такую функцию:
let set = new System.Collections.Generic.HashSet()
let hasBeenAlreadyCalledWithThisType<'T> = not <| set.Add(typeof<'T>.Name)

hasBeenAlreadyCalledWithThisType<int>   //false
hasBeenAlreadyCalledWithThisType<float> //false
hasBeenAlreadyCalledWithThisType<int>   //true
Эта функция будет вести список всех типов, с которыми она была вызвана и отвечать true/false в зависимости от того, "знаком" ли ей уже текущий тип.
Подробнее о type functions вы можете почитать в спецификации F#.

[<AutoOpen>]

Я просто процитирую спецификацию:
When applied to an assembly and given a string  argument, causes the namespace or module to be opened automatically when the assembly is referenced.  When applied to a module without a string argument, causes the module to be opened automatically when the enclosing namespace or module is opened. 

Компилятор, голос!

Можно заставить компилятор выдавать заданные нами сообщения с помощью атрибута
[<CompilerMessage("SomeUserSpecifiedMessage", MessageNumber)>].


P.S.: Были кое-какие проблемы с отображением кода F#, я решил их. При использовании F# brush для SyntaxHighlighter'а нужно закомментить строки про symbolic-класс, иначе будут глюки.

2 комментария:

  1. на самом деле индексаторы это не "официальная" F#, просмотри ещё раз получше MSDN

    ОтветитьУдалить
  2. Посмотрел. Про "неофициальность" их нигде не увидел.
    Смотрим спецификацию:
    "A property member is an indexer property if patidx is not the unit pattern (). Indexer properties called Item are special in the sense that they are accessible via the .[] notation. An Item property that takes one argument is accessed by using x.[i]; with two arguments by x.[i,j], and so on. Setter properties must return type unit."
    Итак, с чего вы взяли то, что вы взяли?))

    ОтветитьУдалить