2015-07-17 8 views
12

Próbowałem zrobić kilka rzeczy zeszłej nocy wokół przyjęcia i wywołania funkcji ogólnej (tj. Typ jest znany w witrynie połączenia , ale potencjalnie różni się w różnych witrynach wywołań, więc definicja powinna być ogólna dla wszystkich arach).Scala Function.tupled i Function.untupled odpowiednik dla zmiennej arity, lub wywoływanie funkcji arith zmiennej z krotką

Załóżmy na przykład, że mam funkcję f: (A, B, C, ...) => Z. (W rzeczywistości istnieje wiele takich f s, których nie znam z góry, a więc nie mogę naprawić typów ani liczby A, B, C, ..., Z.)

Próbuję osiągnąć następujące.

  1. Jak mogę zadzwonić f rodzajowo z instancją (A, B, C, ...)? Jeśli podpis f był znany z góry, mógłbym zrobić coś z udziałem Function.tupled f lub odpowiednika.

  2. Jak zdefiniować inną funkcję lub metodę (na przykład, pewną metodę apply) z tym samym podpisem co f? To znaczy, jak zdefiniować typ g, dla którego sprawdzany jest typ g(a, b, c, ...) tylko wtedy, gdy sprawdzany jest typ? Szukałem tego w Shapeless's HList. Z tego, co mogę do tej pory stwierdzić, HList rozwiązuje przynajmniej kwestię "reprezentowania arbitralnej listy argumentów arity", a także, że Shapeless rozwiąże konwersję do i z kwestii krotki. Jednak nadal nie jestem pewien, czy rozumiem, jak to by pasowało do funkcji ogólnej arii, jeśli w ogóle.

  3. Jak zdefiniować inną funkcję lub metodę z odpowiednim podpisem typu na f? Największy przykład, który przychodzi mi na myśl, to około h: (A, B, C, ...) => SomeErrorThing[Z] \/ Z.

Pamiętam, jak jakiś czas temu oglądałem prezentację konferencji na temat Shapeless. Podczas gdy prezenter nie wykazał tego wyraźnie, to co oni pokazali (różne techniki dotyczące abstrakcji/generalizacji krotek w stosunku do HList s) mogłoby doprowadzić mnie do przekonania, że ​​podobne rzeczy jak powyższe są możliwe przy użyciu tych samych narzędzi.

Z góry dziękuję!

+0

Jestem przekonany, że Shapeless pomoże ci to zrobić. Niestety nie mam teraz czasu, żeby się z tym bawić. – gzm0

+0

Mam wrażenie, że podobnie jak to, co widziałem, robi to Shapeless, ale nie jestem pewien jak. – Ming

Odpowiedz

11

Tak, Bezkształtne mogą ci w tym pomóc. Załóżmy na przykład, że chcemy przyjąć funkcję arbitralnej arii i przekształcić ją w funkcję o tym samym charakterze, ale z typem zwrotu zawiniętym w Option (myślę, że to trafi we wszystkie trzy punkty twojego pytania).

Aby wszystko było proste, powiem tylko, że Option jest zawsze Some. Trwa to dość gęsty cztery linie:

import shapeless._, ops.function._ 

def wrap[F, I <: HList, O](f: F)(implicit 
    ftp: FnToProduct.Aux[F, I => O], 
    ffp: FnFromProduct[I => Option[O]] 
): ffp.Out = ffp(i => Some(ftp(f)(i))) 

Możemy pokazać, że to działa:

scala> wrap((i: Int) => i + 1) 
res0: Int => Option[Int] = <function1> 

scala> wrap((i: Int, s: String, t: String) => (s * i) + t) 
res1: (Int, String, String) => Option[String] = <function3> 

scala> res1(3, "foo", "bar") 
res2: Option[String] = Some(foofoofoobar) 

Uwaga odpowiednie typy statyczne Return. Teraz jak to działa:

FnToProduct klasa typu dostarcza dowodów, że jakiś rodzaj F jest FunctionN (z jakiegoś N), które mogą być przekształcone w zależności od jakiegoś HList do oryginalnego typu wyjściowego.Funkcja HList (a dokładniej Function1) jest członem typu typu Out lub drugim typem pomocnika pomocniczego FnToProduct.Aux.

FnFromProduct nie odwróconej to dowód, że niektóre F jest Function1 z HList pewnego typu wyjściowego, który może być przekształcony w funkcji pewnej Arity do tego typu produkcji.

wrap W naszej metodzie używamy FnToProduct.Aux aby ograniczyć Out instancji FnToProduct dla F w taki sposób, że możemy odnosić się do listy HList parametrów i typu O wynik w rodzaju naszej FnFromProduct instancji. Wdrożenie jest wówczas dość proste - wystarczy zastosować instancje w odpowiednich miejscach.

To wszystko może wydawać się bardzo skomplikowane, ale gdy już raz pracowałeś z tego rodzaju programowaniem generalnym w Scala, stało się ono mniej lub bardziej intuicyjne, a my z przyjemnością odpowiemy na bardziej szczegółowe pytania dotyczące Twojego przypadek użycia.

+0

Dzięki za wyjaśnienie poszczególnych elementów, które idą do tworzenia produktu końcowego! – Ming

+0

Trochę obserwacji, z ciekawości: Co ogranicza relację między 'F' i' I'? Innymi słowy, co uniemożliwia użycie 'wrap' z niekompatybilnymi typami, powiedzmy na przykład' (A, B) 'i' (A, B, C) => D'? Czy to jest fakt, że nie mogę uzyskać instancji 'FnToProduct' /' FnFromProduct' dla typów, które nie są zgodne? Co powstrzymuje mnie przed stworzeniem tego typu, przypadkowo lub w inny sposób? Innymi słowy, co odróżnia "poprawne" (lub konwencjonalnie dopuszczalne) przypadki 'FnToProduct' /' FnFromProduct'? – Ming

+1

Shapeless poda poprawne wystąpienia tych klas. Przypuszczam, że możesz podać swoje własne nieprawidłowe, ale tak jest w przypadku każdej (niezamocowanej) klasy typu. Dlaczego te klasy nie są zapieczętowane to dobre pytanie - może to być po prostu niewygodne, biorąc pod uwagę sposób, w jaki działa generowanie kodu Shapeless. W każdym razie zazwyczaj nie martwisz się przypadkowym tworzeniem nieprawidłowych instancji takich klas typów (i nie powinno to w ogóle oznaczać twoich własnych instancji). –