Ограничения типа F # и разрешение перегрузки - PullRequest
5 голосов
/ 26 марта 2012

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

type PrintableInt(x:int) =
  member this.Print() = printfn "%d" x

let (!) x = PrintableInt(x)

type Printer() =
  static member inline Print< ^a when ^a : (member Print : Unit -> Unit)>(x : ^a) =
    (^a : (member Print : Unit -> Unit) x)
  static member inline Print((x,y) : 'a * 'b) =
    Printer.Print(x)
    Printer.Print(y)

let x = (!1,!2),(!3,!4)

Printer.Print(x)

Есть ли способ сделать это? Я делаю это в контексте разработки игр, поэтому я не могу позволить себе накладные расходы на рефлексию, перепечатывание и динамическое приведение: либо я делаю это статически с помощью встраивания, либо не делаю это вообще: (

Ответы [ 2 ]

8 голосов
/ 26 марта 2012

То, что вы пытаетесь сделать, возможно.Вы можете эмулировать классы типов в F #, как сказал Томас, возможно, это не так идиоматично, как в Haskell.Я думаю, что в вашем примере вы смешиваете классы типов с типизацией утили, если вы хотите использовать подход классов типов, не используйте члены, вместо этого используйте функции и статические члены.:

type Print = Print with    
  static member ($) (_Printable:Print, x:string) = printfn "%s" x
  static member ($) (_Printable:Print, x:int   ) = printfn "%d" x
  // more overloads for existing types

let inline print p = Print $ p

type Print with
  static member inline ($) (_Printable:Print, (a,b) ) = print a; print b

print 5
print ((10,"hi"))
print (("hello",20), (2,"world"))

// A wrapper for Int (from your sample code)
type PrintableInt = PrintableInt of int with
  static member ($) (_Printable:Print, (PrintableInt (x:int))) = printfn "%d" x

let (!) x = PrintableInt(x)

let x = (!1,!2),(!3,!4)

print x

// Create a type
type Person = {fstName : string ; lstName : string } with
  // Make it member of _Printable
  static member ($) (_Printable:Print, p:Person) = printfn "%s, %s" p.lstName p.fstName

print {fstName = "John"; lstName = "Doe" }
print (1 ,{fstName = "John"; lstName = "Doe" })

Примечание. Я использовал оператор, чтобы избежать записи ограничений вручную, но в этом случае также возможно использовать именованный статический член.Подробнее об этой технике здесь .

4 голосов
/ 26 марта 2012

То, что вы пытаетесь сделать, невозможно ( edit: очевидно, это можно сделать - но это может быть не идиоматический F #), потому что язык ограничений не может захватитьограничения, необходимые для второй операции Print.В принципе, невозможно написать рекурсивные ограничения, говоря, что:

Пусть C будет ограничением, указывающим, что тип либо предоставляет Print, либо это кортеж из двух элементовгде каждый элемент удовлетворяет C .

F # не поддерживает классы типов, и поэтому большинство попыток эмулировать их будут (вероятно) каким-то образом ограничены или будут выглядеть оченьнеестественно.На практике вместо того, чтобы пытаться эмулировать решения, которые работают на других языках, лучше искать идиоматическое F # решение проблемы.

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

...