Общее извлечение из конструктора

В 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

person user710426    schedule 15.04.2011    source источник
comment
При первой публикации этого не было, но теперь работает отступ в четыре пробела. Странный.   -  person user710426    schedule 15.04.2011


Ответы (4)


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

Вы можете :

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

  • используйте метапрограммирование для создания разных версий peel для каждого типа. Например, с помощью инструмента OCaml type-conv у вас может быть 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 и т. д., но единый интерфейс доступа.

person gasche    schedule 15.04.2011
comment
Re: Ваше первое предложение, конечно же, будет 'a -> 'b! :-) - person Daniel; 16.04.2011
comment
Я не знал, что ты умеешь let (Blah whatever) = , но это работает! - person user710426; 16.04.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
person Laurent    schedule 15.04.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 Лорана.

person petebu    schedule 15.04.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"

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

person Daniel    schedule 15.04.2011