2016-03-01 17 views
9

Mam prosty wzór fabryczny, w którym implementacja jest określana poprzez rozdzielczość przeciążenia. Problem polega na tym, że kompilator Kotlin narzeka z "niejednoznacznością rozdzielczości przeładowania" dla wbudowanej lambda.Kotlin: Inline lambda i przeciążenie rozdzielczości niejednoznaczności

class Foo(){ 
    companion object Factory { 
     fun create(x: Int, f: (Int) -> Double) = 2.0 
     fun create(x: Int, f: (Int) -> Int) = 1 
    } 
} 

fun main(args:Array<String>){ 
    val a = Foo.create(1,::fromDouble) //OK 
    val b = Foo.create(1,::fromInt) //OK 
    val ambiguous = Foo.create(1){i -> 1.0} //Overload resolution ambiguity? 
} 


fun fromDouble(int:Int) = 1.0 
fun fromInt(int:Int) = 1 

W jaki sposób kompilator Kotlin rozwiązuje problem przeciążenia i dlaczego liniowa wartość lambda jest niejednoznaczna?

+4

To wydaje się być błędem, ponieważ jeśli rzucę lambdę w stylu '{i: Int -> 1.0} jako (Int) -> Double', nie ma dwuznaczności, ale mówi się, że rzutowanie nie jest potrzebne. Ponadto, jeśli wypakowuję lambda do 'val l = {i: Int -> 1.0}' i używam go, znowu nie ma dwuznaczności. Wyszukaj w tym problemie moduł do śledzenia błędów, a jeśli go nie ma, zrób nowy: https://youtrack.jetbrains.com/issues/KT – hotkey

+1

Kolejną interesującą rzeczą jest to, że jeśli rzucisz lambdę jako @hotkey, IDE powie ty, że to nie jest konieczne. Ale w momencie, gdy go usuniesz, narzeka na niejednoznaczność. –

+1

Dzięki za wejście! Pomyślałem, że to prawdopodobnie błąd. Złożył raport na https://youtrack.jetbrains.com/issue/KT-11265 –

Odpowiedz

4

Kompilator Kotlin rozwiązuje każde wyrażenie tylko jeden raz. Tak więc, gdy kompilator rozpoczyna rozdzielczość dla wyrażenia lambda, powinien znać typy argumentów lambda. Z tego powodu kompilator powinien wybrać jedną z metod createprzed zaczyna wyglądać wewnątrz lambda.

Przykład:

fun foo(f: (Int) -> Int) = 1 
fun foo(f: (String) -> String) = "" 
val bar = foo { 
    println(it) 
    5 
} 

Tutaj nie możemy wybrać jedną z funkcji foo bo nikt z nich jest bardziej szczegółowy, że inny, więc nie możemy rozpocząć rozdzielczość dla wyrażenia lambda, ponieważ nie wiemy, wpisz dla it.

W twoim przykładzie teoretycznie możliwe jest rozpoczęcie rozdzielczości dla lambda przed wyborem konkretnej funkcji, ponieważ dla wszystkich potencjalnych funkcji typy argumentów lambda są takie same. Ale jest to logika niesprawna, która może być trudna do zrealizowania.

+0

To bummer. Rozdzielczość przeciążeniowa dla lambda jest bardzo przydatna do zastosowań numerycznych. Powiedzmy, że mam klasę macierzy, która działa na float, double i int. Przy "rozdzielczości przeciążania" instancją byłoby 'val ms = Mat44 {i, j -> 1.0}', teraz jestem zmuszony napisać 'val ms = Mat44 ({i: Int, j: Int -> 1.0} as (Int, Int) -> Double) 'co jest po prostu proste i głupie verbose dla tak trywialnego zadania. Wielkie dzięki @erokhins za świetne wyjaśnienie! –

+0

Jako obejście można utworzyć funkcje o różnych nazwach: 'fun Mat44Int2Double (f: (Int, Int) -> Double) = Mat44 (f)'. Ale to nie jest eleganckie ... – erokhins

+0

Tak, przypomina mi to stare dobre dni ANSI C :) –

Powiązane problemy