Общее извлечение из конструктора - PullRequest
1 голос
/ 15 апреля 2011

В F # и OCaml я пишу много кода вроде

type C = Blah of Whatever  
let d = Blah (createWhatever ())  // so d is type C  
...  
let x = match d with | Blah b -> b

Что бы мне понравилось, это

...  
let x = peel d

Где пилинг будет работать для любой конструктор / дискриминатор.
Конечно, я не единственный, кого это раздражает.
edit: Хорошие ответы, но у меня нет представителя, чтобы голосовать за них.Как насчет этой ситуации?

member self.Length = match self with | L lab -> lab.Length

Ответы [ 4 ]

5 голосов
/ 16 апреля 2011

Невозможно сделать это безопасно: если бы peel была функцией, каков был бы ее тип? Он не может быть напечатан и, следовательно, не может быть «хорошим парнем» в языке.

Вы можете:

  • используйте функции отражения (в F #) или функции разрыва типов (в OCaml это модуль Obj), но вы получите что-то небезопасное с неточным типом, так что это довольно уродливо и «используйте на свой страх и риск» «

  • используйте метапрограммирование для генерации различных версий peel для каждого типа для вас. Например, используя инструмент type-conv OCaml, вы можете type blah = Blah of something неявно определить функцию peel_blah и type foo = Foo of something определить peel_foo.

Лучшее решение, имхо, это ... во-первых, не нуждаться в таком peel. Я вижу две возможности:

  • Вы можете использовать умные шаблоны вместо функции: при использовании let (Blah whatever) = f x или fun (Blah whatever) -> ... функция распаковки вам больше не нужна.

  • Или вы можете вместо type blah = Blah of what написать

    type blah = (blah_tag * whatever) and blah_tag = Blah

    Таким образом, у вас есть не тип суммы, а тип продукта (вы пишете (Blah, whatever)), а ваш peel равен snd. У вас по-прежнему есть свой (несовместимый) тип для каждого blah, foo и т. Д., Но унифицированный интерфейс доступа.

4 голосов
/ 16 апреля 2011

Как уже упоминалось, let удобно для сопоставления с образцом.Если вы хотите получить доступ к значению в середине выражения, где шаблоны не допускаются, я предлагаю добавить член к типам:

type C = Blah of int
with member c.Value = match c with Blah x -> x

let x = Blah 5
let y = Blah 2
let sum = x.Value + y.Value
1 голос
/ 16 апреля 2011

Я бы написал это вместо:

type C = Blah of Whatever  
let d = Blah (createWhatever ())  // so d is type C  
...
let (Blah x) = d

Для вашей второй ситуации мне нравится Лоран member x.Value = match x with Blah v -> v.

0 голосов
/ 15 апреля 2011

Работает для DU ... потребуется настройка для работы с конструкторами классов:

open Microsoft.FSharp.Reflection

let peel d = 
    if obj.ReferenceEquals(d, null) then nullArg "d"
    let ty = d.GetType()
    if FSharpType.IsUnion(ty) then
        match FSharpValue.GetUnionFields(d, ty) with
        | _, [| value |] -> unbox value
        | _ -> failwith "more than one field"
    else failwith "not a union type"

Кстати: я бы обычно не делал что-то подобное, но раз ты спросил ...

...