2016-01-19 14 views
11

The Perl 6 Web site on functions saysQual è il punto di coercizione come Int (Cool)?

tipi coercizione possono aiutare ad avere un tipo specifico all'interno di una routine, ma accettare l'input più ampio. Quando viene chiamata la routine, l'argomento viene automaticamente convertito nel tipo più stretto.

sub double(Int(Cool) $x) { 
    2 * $x 
} 

say double '21'; # 42 
say double Any;  # Type check failed in binding $x; expected 'Cool' but got 'Any' 

Qui l'Int è il tipo di destinazione a cui verrà costretto l'argomento, e Cool è il tipo che la routine accetta come ingresso.

Ma qual è il punto per il sub? Non è $x solo un Int? Perché dovresti limitare il chiamante ad implementare Cool per l'argomento?

Sono doppiamente confuso dall'esempio perché Int già is Cool. Così ho fatto un esempio in cui i tipi non condividono una gerarchia:

class Foo { method foomethod { say 'foomethod' } } 
class Bar {} 

class Quux is Foo { 
# class Quux { # compile error 
    method Bar { Bar.new } 
} 

sub foo(Bar(Foo) $c) { 
    say $c.WHAT; # (Bar) 
    # $c.foomethod # fails if uncommented: Method 'foomethod' not found for invocant of class 'Bar' 
} 

foo(Quux.new) 

Qui l'invocant di foo è limitato a fornire una Foo che può essere convertito in un Bar ma foo non può nemmeno chiamare un metodo di Foo su $c perché il suo tipo èBar. Quindi, perché foo importa che il tipo to-be-coerced sia un Foo in primo luogo?

Qualcuno potrebbe far luce su questo? Anche i link alla documentazione appropriata e parti della specifica sono apprezzati. Non ho trovato nulla di utile lì.

+1

non si limita al chiamante di * anche * implementare 'Cool', si limita a * solo * implementare' Cool' e di fornire un metodo di coercizione; 'double '21'' passa in una' Str', ma callee-side arriva un 'Int' – Christoph

+0

@Christoph I ha lasciato il" also ". Spero sia accurato ora. – musiKk

+0

Mi sembra che tu stia capendo all'indietro. La firma 'Int (Cool)' * accetta * a 'Cool' (il tipo più generale) e promuove a un' Int' localmente, non viceversa. Quindi puoi dargli un 'Str' e lo convertirà in un' Int', che * non * accade se specifichi 'Int' nella firma. Continui a parlare di inconvenienti al chiamante - quale inconveniente è il chiamante vivendo non essendo costretto a fare da solo la coercizione? – darch

risposta

4

Specificare tipo di parametro, senza coercizione: Int $x

Potremmo dichiarare:

sub double (Int $x) { ... } # Accept only Int. (No coercion.) 

Quindi questo dovrebbe funzionare:

double(42); 

Ma purtroppo digitando 42 in risposta a questo:

double(prompt('')); # `prompt` returns the string the user types 

fa sì che il double chiamata a fallire con Type check failed in binding $x; expected Int but got Str ("42") perché 42, guardando come un numero, è tecnicamente una stringa di tipo Str, e abbiamo chiesto per nessuna coercizione.

Specificare tipo di parametro, con una coperta la coercizione: Int() $x

possiamo introdurre coltre coercizione di qualsiasi valore a firma del sub:

sub double (Int(Any) $x) { ... } # Take Any value. Coerce to an Int. 

Oppure:

sub double (Int() $x) { ... } # Same -- `Int()` coerces from Any. 

Ora, se si digitare 42 quando richiesto dall'istruzione double(prompt(''));, l'errore di controllo del tipo di esecuzione non si applica più e instea d il tempo di esecuzione tenta di forzare la stringa a un Int. Se l'utente digita un numero ben formato, il codice funziona correttamente. Se si digita 123abc la coercizione non riuscirà a run-time con un messaggio di errore bella:

Cannot convert string to number: trailing characters after number in '123⏏abc' 

Un problema con la coperta la coercizione di qualsiasi valore è che il codice come questo:

class City { ... } # City has no Int coercion 
my City $city; 
double($city); 

non riesce a run- tempo con il messaggio: "Metodo 'Int' non trovato per invocante di classe 'Città'".

Specificare tipo di parametro, con la coercizione da valori fresco: Int(Cool) $x

Siamo in grado di scegliere un punto di equilibrio tra coercizione e coperta la coercizione di qualsiasi valore.

La classe migliore per costringere dal spesso è la classe Cool, perché i valori freddi sono garantiti a uno costringere bene ad altri tipi di base o generare un bel messaggio di errore:

# Accept argument of type Cool or a subclass and coerce to Int: 
sub double (Int(Cool) $x) { ... } 

Con questa definizione, il seguente:

double(42); 
double(prompt('')); 

funziona come bene come si può, e:

double($city); 

non riesce con "Controllo tipo fallito nell'associazione $ x; previsto Cool but ottenuto City (City)", che è probabilmente un po 'meglio diagnostico per il programmatore di 'Metodo 'Int' non trovato per invocant di classe 'città''.


perché sarebbe foo cura che il tipo di a-essere-costretto è un Foo, in primo luogo?

Speriamo che sia ormai evidente che l'unica ragione per cui vale la pena di limitare la coerce-da-tipo a Foo è perché questo è un tipo dovrebbe costringere con successo per un valore Bar (o, forse, fallire con un messaggio amichevole).

Qualcuno potrebbe far luce su questo? Anche i link alla documentazione appropriata e parti della specifica sono apprezzati. Non ho trovato nulla di utile lì.

Il documento che hai originariamente citato è praticamente tutto quello che c'è per il docente. Speriamo che abbia senso ora e tu sei tutto pronto. Se no, per favore commenta e andremo da lì.

+0

Credo di aver capito ora. È ancora un po 'arbitrario per me, ma probabilmente dovrò avere più esperienza. Quindi non sarebbe utile lanciare un errore di compilazione (attenzione?) Se si tenta di compilare 'X (Y)' dove 'Y' non è un sottotipo di' X'? E conosci altri esempi utili oltre a "Int (Cool)'? – musiKk

+0

Perl 6 rende disponibile per i codificatori sia il controllo statico (in fase di compilazione) che dinamico (in fase di esecuzione).In generale è statico per i sottomarini e dinamico per i metodi. Ma puoi spostare le cose in qualsiasi direzione abbia senso per un dato caso d'uso. Una semplice dichiarazione di 'Int $ x' per un sottocampione indirizza Perl 6 a controllare staticamente che ogni argomento di corrispondenza sia un' Int' * o una sottoclasse di 'Int' *. Se questo è il miglior sottotitolo disponibile e non riesce ancora a digitare check, si ottiene un errore in fase di compilazione. – raiph

+0

Nota che multidispatch, come discusso in [la sezione Perl 6 della pagina di wikipedia su più dispatch] (https://en.wikipedia.org/wiki/Multiple_dispatch#Perl_6), in [doc.perl6.org/language/functions ] (http://doc.perl6.org/language/functions#Multi-dispatch) e nei documenti di progettazione della lingua, con la maggior parte in [S12: Multisubs and Multimethods] (http: //design.perl6. org/S12.html # Multisubs_and_Multimethods), è praticamente rilevante qui come driver delle funzionalità di controllo dei tipi (anche se teoricamente non è perché le funzionalità di controllo del tipo funzionano allo stesso modo per la normale distribuzione singola). – raiph

2

Mi manca qualcosa? Non sono un esperto di Perl 6, ma sembra che la sintassi consenta di specificare indipendentemente sia quali tipi di input sono consentiti e come l'input verrà presentato alla funzione.

Restringere l'input consentito è utile perché significa che il codice genererà un errore, piuttosto che una conversione di tipo silenzioso (inutile) quando la funzione viene chiamata con un parametro non sensato.

Non credo che un esempio in cui i due tipi non siano in una relazione gerarchica ha senso.

4

Quello che fa è accettare un valore che è un sottotipo di fresco, e cerca di trasformarlo in un Int. A quel punto lo è un Int indipendentemente da cosa fosse prima.

Così

sub double (Int(Cool) $n) { $n * 2 } 

può davvero essere pensato come (credo che questo è il modo in realtà è stato implementato in Rakudo)

# Int is a subtype of Cool otherwise it would be Any or Mu 
proto sub double (Cool $n) {*} 

# this has the interior parts that you write 
multi sub double ( Int $n) { $n * 2 } 

# this is what the compiler writes for you 
multi sub double (Cool $n) { 
    # calls the other multi since it is now an Int 
    samewith Int($n); 
} 

Quindi questo accetta qualsiasi Int, Str , Ratto, FatRat, Num, Array, Hash, ecc e cerca di convertirlo in un Int prima di chiamare &infix:<*> con esso, e 2.

say double ' 5 '; # 25 
say double 2.5;  # 4 
say double [0,0,0]; # 6 
say double { a => 0, b => 0 }; # 4 

Si potrebbe limitare ad un fredda invece di Qualsiasi come tutti fredde valori sono essenzialmente tenuti a fornire una coercizione per Int.

(:(Int(Any) $) può essere ridotto ad appena :(Int() $))


La ragione si potrebbe fare questo è che hai bisogno di essere un Int all'interno del sub perché si sta chiamando altro codice che fa cose diverse con differenti tipi.

sub example (Int(Cool) $n) returns Int { 
    other-multi($n) * $n; 
} 

multi sub other-multi (Int $) { 10 } 
multi sub other-multi (Any $) { 1 } 

say example 5; # 50 
say example 4.5; # 40 

In questo caso particolare si potrebbe scrivere come uno di questi

sub example (Cool $n) returns Int { 
    other-multi(Int($n)) * Int($n); 
} 

sub example (Cool $n) returns Int { 
    my $temp = Int($n); 
    other-multi($temp) * $temp; 
} 

sub example (Cool $n is copy) returns Int { 
    $n = Int($n); 
    other-multi($n) * $n; 
} 

nessuno di loro è chiara come quella che utilizza la firma di costringere per voi.


Normalmente per una funzione così semplice è possibile utilizzare uno di questi e probabilmente farà ciò che si desidera.

my &double = * * 2; # WhateverCode 
my &double = * × 2; # ditto 

my &double = { $_ * 2 };  # bare block 
my &double = { $^n * 2 };  # block with positional placeholder 
my &double = -> $n { $n * 2 }; # pointy block 

my &double = sub ($n) { $n * 2 } # anon sub 
my &double = anon sub double ($n) { $n * 2 } # anon sub with name 

my &double = &infix:<*>.assuming(*,2); # curried 
my &double = &infix:<*>.assuming(2); 

sub double ($n) { $n * 2 } # same as :(Any $n) 
+0

So * cosa * fa. Voglio sapere * perché * ne avremmo bisogno. Hai una frase con "ragione" in esso e io proprio non capisco. Perché dovrei "essenzialmente" richiedere al tipo di fornire una coercizione a 'Int' quando' Int() 'di per sé specifica già tale requisito in modo univoco? (Inoltre non ho familiarità con 'proto' e le informazioni su di esso sono molto difficili da trovare. Sembra che si tratti di prototipi ...) – musiKk

+0

@musiKk Quando si chiama un multi-sub, si sta effettivamente chiamando un sub-proto che poi chiama il multi appropriato candidato. –

+0

Ancora una volta, sei di grande aiuto nel rispondere al "cosa". Voglio sapere "perché" potresti potenzialmente scomodare il chiamante. In 'Socket :: INET' uno dei metodi vuole un' Cool', un altro 'Int (Cool)'. Entrambe le volte è semanticamente il numero di byte. Probabilmente è fuori portata per questa domanda; Voglio solo capire le ragioni, quindi so cosa fare quando progetto le API da solo. – musiKk

0

Credo che la risposta sia tanto semplice quanto non si potrebbe voler limitare l'argomento a Int anche se lo si considererà come Int all'interno del sottotitolo. per qualche motivo si desidera essere in grado di moltiplicare una matrice con un hash, ma fallire se gli argomenti non possono essere trattati come Int (ad esempio non è Cool).

my @a = 1,2,3; 
my %h = 'a' => 1, 'b' => 2; 
say @a.Int; # 3 (List types coerced to the equivalent of .elems when treated as Int) 
say %h.Int; # 2 

sub m1(Int $x, Int $y) {return $x * $y} 
say m1(3,2); # 6 
say m1(@a,%h); # does not match 

sub m2(Int(Cool) $x, Int(Cool) $y) {return $x * $y} 
say m2('3',2); # 6 
say m2(@a,%h); # 6 
say m2('foo',2); # does not match 

naturalmente, si potrebbe anche fare questo senza la firma perché l'operazione matematica sarà costringere il tipo automaticamente:

sub m3($x,$y) {return $x * $y} 
say m3(@a,%h); # 6 

tuttavia, questo rinvia il vostro controllo di tipo all'interno del sub, che tipo di sconfigge lo scopo di una firma e ti impedisce di fare il sub a multi

0

tutti i sottotipi di Cool saranno (come fresco impone loro) costretto a un Int. Quindi, se un operatore o una routine interna al tuo sub funziona solo con gli argomenti Int, non devi aggiungere un'istruzione/espressione aggiuntiva che converta in un Int né tale codice operatore/routine deve tenere conto di altri sottotipi di Cool. Impone che l'argomento sia un Int all'interno del tuo sub ovunque lo si usi.

Il vostro esempio è indietro:

class Foo { method foomethod { say 'foomethod' } } 
class Bar {} 

class Quux is Bar { 
    method Foo { Foo.new } 
} 

sub foo(Foo(Bar) $c) { 
#= converts $c of type Bar to type Foo 
#= returns result of foomethod 
    say $c.WHAT; #-> (Foo) 
    $c.foomethod #-> foomethod 
} 

foo(Quux.new)