Quali sono le regole precise per quando è ansible omettere parentesi, punti, parentesi graffe, = (funzioni), ecc.?

Quali sono le regole precise per quando puoi omettere (omettere) parentesi, punti, parentesi graffe, = (funzioni), ecc.?

Per esempio,

(service.findAllPresentations.get.first.votes.size) must be equalTo(2). 
  • service è il mio object
  • def findAllPresentations: Option[List[Presentation]]
  • votes restituiti List[Vote]
  • devono e sono entrambe le funzioni delle specifiche

Perché non posso andare:

 (service findAllPresentations get first votes size) must be equalTo(2) 

?

L’errore del compilatore è:

“RestServicesSpecTest.this.service.findAllPresentations di tipo Option [List [com.sharca.Presentation]] non accetta parametri”

Perché pensa che sto provando a passare un parametro? Perché devo usare i punti per ogni chiamata al metodo?

Perché deve (service.findAllPresentations get first votes size) essere uguale A (2) comportare:

“non trovato: valore prima”

Tuttavia, “deve essere uguale a 2” di (service.findAllPresentations.get.first.votes.size) deve essere uguale a 2, cioè, il concatenamento del metodo funziona correttamente? – object catena catena param.

Ho esaminato il libro e il sito web di Scala e non riesco a trovare una spiegazione esauriente.

È in realtà, come spiega Rob H in Stack Overflow question Quali personaggi posso omettere in Scala? , che l’unico caso d’uso valido per omettere il “.” è per le operazioni in stile “operando dell’operando dell’operatore” e non per il concatenamento del metodo?

Sembra che tu ti sia imbattuto nella risposta. Ad ogni modo, cercherò di chiarire.

È ansible omettere i punti quando si utilizzano le notazioni prefisso, infisso e suffisso – la cosiddetta notazione dell’operatore . Durante l’uso della notazione dell’operatore, e solo in quel momento, è ansible omettere la parentesi se sono passati meno di due parametri al metodo.

Ora, la notazione dell’operatore è una notazione per la chiamata al metodo , il che significa che non può essere utilizzata in assenza dell’object che viene chiamato.

Illustrerò brevemente le notazioni.

Prefisso:

Solo ~ ! , + e - possono essere utilizzati nella notazione prefisso. Questa è la notazione che stai usando quando scrivi !flag o val liability = -debt .

Infix:

Questa è la notazione in cui compare il metodo tra un object e i suoi parametri. Gli operatori aritmetici si adattano tutti qui.

Postfix (anche suffisso):

Quella notazione è usata quando il metodo segue un object e non riceve parametri . Ad esempio, puoi scrivere la list tail e questa è la notazione postfissa.

È ansible concatenare le chiamate di notazione infless senza problemi, a condizione che nessun metodo sia al curry. Ad esempio, mi piace usare il seguente stile:

 (list filter (...) map (...) mkString ", " ) 

È la stessa cosa di:

 list filter (...) map (...) mkString ", " 

Ora, perché sto usando le parentesi qui, se filtro e mappa prendono un singolo parametro? È perché sto passando loro delle funzioni anonime. Non riesco a combinare le definizioni di funzioni anonime con lo stile infisso perché ho bisogno di un limite per la fine della mia funzione anonima. Inoltre, la definizione dei parametri della funzione anonima potrebbe essere interpretata come l’ultimo parametro del metodo infisso.

Puoi usare infix con più parametri:

 string substring (start, end) map (_ toInt) mkString ("<", ", ", ">") 

Le funzioni al curry sono difficili da usare con la notazione infix. Le funzioni di piegatura sono un chiaro esempio di ciò:

 (0 /: list) ((cnt, string) => cnt + string.size) (list foldLeft 0) ((cnt, string) => cnt + string.size) 

È necessario utilizzare le parentesi al di fuori della chiamata infisso. Non sono sicuro delle regole esatte in gioco qui.

Ora parliamo di postfix. Postfix può essere difficile da usare, perché non può mai essere usato ovunque tranne la fine di un’espressione . Ad esempio, non puoi fare quanto segue:

  list tail map (...) 

Perché la coda non appare alla fine dell’espressione. Non puoi farlo neanche:

  list tail length 

È ansible utilizzare la notazione infix utilizzando le parentesi per contrassegnare la fine delle espressioni:

  (list tail) map (...) (list tail) length 

Si noti che la notazione postfix è sconsigliata perché potrebbe non essere sicura .

Spero che questo abbia chiarito tutti i dubbi. In caso contrario, basta lasciare un commento e vedrò cosa posso fare per migliorarlo.

Definizioni di class:

val o var possono essere omessi dai parametri di class che renderanno il parametro privato.

L’aggiunta di var o val farà sì che sia pubblico (ovvero, vengono creati metodi di accesso e mutatori).

{} può essere omesso se la class non ha corpo, cioè

 class EmptyClass 

Istanziazione di class:

I parametri generici possono essere omessi se possono essere dedotti dal compilatore. Tuttavia, se i tipi non corrispondono, il parametro type viene sempre dedotto in modo che corrisponda. Quindi, senza specificare il tipo, potresti non ottenere ciò che ti aspetti – cioè, dato

 class D[T](val x:T, val y:T); 

Questo ti darà un errore di tipo (Int trovato, previsto String)

 var zz = new D[String]("Hi1", 1) // type error 

Mentre questo funziona bene:

 var z = new D("Hi1", 1) == D{def x: Any; def y: Any} 

Perché il parametro type, T, viene inferito come il supertipo meno comune dei due – Any.


Definizioni di funzioni:

= può essere eliminato se la funzione restituisce Unità (nulla).

{} per il corpo della funzione può essere eliminato se la funzione è una singola istruzione, ma solo se l’istruzione restituisce un valore (è necessario il segno = ), cioè,

 def returnAString = "Hi!" 

ma questo non funziona:

 def returnAString "Hi!" // Compile error - '=' expected but string literal found." 

Il tipo di ritorno della funzione può essere omesso se può essere dedotto (un metodo ricorsivo deve avere specificato il suo tipo di ritorno).

() può essere eliminato se la funzione non accetta argomenti, cioè,

 def endOfString { return "myDog".substring(2,1) } 

che per convenzione è riservato a metodi che non hanno effetti collaterali – ne riparleremo più avanti.

() non viene effettivamente eliminato di per sé quando si definisce un passaggio per nome paramenter, ma in realtà è una notazione abbastanza semanticamente diversa, cioè,

 def myOp(passByNameString: => String) 

MyOp accetta un parametro pass-by-name, che risulta in una stringa (ovvero può essere un blocco di codice che restituisce una stringa) rispetto ai parametri di funzione,

 def myOp(functionParam: () => String) 

che dice che myOp accetta una funzione che ha zero parametri e restituisce una stringa.

(Si badi bene, i parametri del pass-by-name vengono compilati in funzioni, ma rende la syntax migliore.)

() può essere eliminato nella definizione del parametro della funzione se la funzione accetta un solo argomento, ad esempio:

 def myOp2(passByNameString:(Int) => String) { .. } // - You can drop the () def myOp2(passByNameString:Int => String) { .. } 

Ma se richiede più di un argomento, devi includere ():

 def myOp2(passByNameString:(Int, String) => String) { .. } 

dichiarazioni:

. può essere abbandonato per utilizzare la notazione dell’operatore, che può essere utilizzata solo per gli operatori infissi (operatori di metodi che accettano argomenti). Vedi la risposta di Daniel per maggiori informazioni.

  • . può anche essere abbandonato per le funzioni di postfix tail

  • () può essere eliminato per gli operatori postfix list.tail

  • () non può essere utilizzato con metodi definiti come:

     def aMethod = "hi!" // Missing () on method definition aMethod // Works aMethod() // Compile error when calling method 

Poiché questa notazione è riservata per convenzione a metodi che non hanno effetti collaterali, come la Coda # dell’elenco (ovvero, l’invocazione di una funzione senza effetti collaterali significa che la funzione non ha alcun effetto osservabile, eccetto per il suo valore di ritorno).

  • () può essere rilasciato per la notazione dell’operatore quando si passa in un singolo argomento

  • () potrebbe essere necessario utilizzare operatori postfix che non sono alla fine di una dichiarazione

  • () può essere richiesto per designare dichiarazioni annidate, fini di funzioni anonime o per operatori che prendono più di un parametro

Quando si chiama una funzione che accetta una funzione, non è ansible omettere la () dalla definizione della funzione interna, ad esempio:

 def myOp3(paramFunc0:() => String) { println(paramFunc0) } myOp3(() => "myop3") // Works myOp3(=> "myop3") // Doesn't work 

Quando si chiama una funzione che accetta un parametro con nome, non è ansible specificare l’argomento come funzione anonima senza parametri. Ad esempio, dato:

 def myOp2(passByNameString:Int => String) { println(passByNameString) } 

Devi chiamarlo come:

 myOp("myop3") 

o

 myOp({ val source = sourceProvider.source val p = myObject.findNameFromSource(source) p }) 

ma no:

 myOp(() => "myop3") // Doesn't work 

IMO, l’uso eccessivo dei tipi di reso in caduta può essere dannoso per il riutilizzo del codice. Basta guardare le specifiche per un buon esempio di leggibilità ridotta a causa della mancanza di informazioni esplicite nel codice. Il numero di livelli di riferimento indiretto per capire veramente quale sia il tipo di una variabile può essere pazzesco. Speriamo che strumenti migliori possano evitare questo problema e mantenere il nostro codice conciso.

(OK, nel tentativo di compilare una risposta più completa e concisa (se ho perso qualcosa, o ottenuto qualcosa di sbagliato / inaccurato, per favore, commento), ho aggiunto all’inizio della risposta. Si prega di notare che questa non è una lingua specifica, quindi non sto cercando di renderlo esattamente accademicamente corretto – più simile a una carta di riferimento.)

Una raccolta di citazioni che danno un’idea delle varie condizioni …

Personalmente, ho pensato che ci sarebbe stato di più nelle specifiche. Sono sicuro che deve esserci, sto solo cercando le parole giuste …

Ci sono comunque un paio di fonti, e le ho raccolte insieme, ma niente di veramente completo / comprensibile / comprensibile / che spieghi i problemi di cui sopra …:

“Se un corpo del metodo ha più di un’espressione, devi circondarlo con parentesi graffe {…}. Puoi omettere le parentesi se il corpo del metodo ha solo un’espressione.”

Dal capitolo 2, “Digita Less, Do More”, di Programming Scala :

“Il corpo del metodo superiore viene dopo il segno di uguale” = “Perché un segno di uguale? Perché non solo parentesi graffe {…}, come in Java? Perché punto e virgola, tipi di funzione restituiti, elenchi di argomenti del metodo e anche le parentesi graffe a volte vengono omessi, l’utilizzo di un segno di uguale impedisce parecchie possibili ambiguità di parsing e l’uso di un segno di uguale ci ricorda che anche le funzioni sono valori in Scala, che è coerente con il supporto di programmazione funzionale di Scala, descritto in maggior dettaglio nel capitolo 8, Programmazione funzionale in Scala.”

Dal capitolo 1, “Zero to Sixty: Introducing Scala”, di Programming Scala :

“Una funzione senza parametri può essere dichiarata senza parentesi, nel qual caso deve essere chiamata senza parentesi.Questo fornisce il supporto per il principio di accesso uniforms, in modo tale che il chiamante non sappia se il simbolo è una variabile o una funzione senza parametri.

Il corpo della funzione è preceduto da “=” se restituisce un valore (cioè il tipo restituito è qualcosa di diverso da Unit), ma il tipo restituito e “=” possono essere omessi quando il tipo è Unit (cioè sembra una procedura al contrario di una funzione).

Non sono necessarie bretelle intorno al corpo (se il corpo è una singola espressione); più precisamente, il corpo di una funzione è solo un’espressione e qualsiasi espressione con più parti deve essere racchiusa tra parentesi graffe (un’espressione con una parte può essere facoltativamente racchiusa tra parentesi graffe). ”

“Le funzioni con zero o un argomento possono essere chiamate senza il punto e le parentesi, ma qualsiasi espressione può avere parentesi intorno ad essa, quindi puoi omettere il punto e usare ancora le parentesi.

E dato che puoi utilizzare le parentesi ovunque puoi usare le parentesi, puoi omettere il punto e inserire le parentesi, che possono contenere più istruzioni.

Le funzioni senza argomenti possono essere chiamate senza le parentesi. Ad esempio, la funzione length () su String può essere invocata come “abc” .length anziché “abc” .length (). Se la funzione è una funzione Scala definita senza parentesi, la funzione deve essere chiamata senza parentesi.

Per convenzione, le funzioni senza argomenti che hanno effetti collaterali, come println, sono chiamate con parentesi; quelli senza effetti collaterali sono chiamati senza parentesi “.

Dal post del blog Scala Syntax Primer :

“Una definizione di procedura è una definizione di funzione in cui il tipo di risultato e il segno di uguale sono omessi, la sua espressione di definizione deve essere un blocco Ad esempio, def f (ps) {stats} equivale a def f (ps): Unit = {stats }.

Esempio 4.6.3 Ecco una dichiarazione e una descrizione di una procedura chiamata write:

 trait Writer { def write(str: String) } object Terminal extends Writer { def write(str: String) { System.out.println(str) } } 

Il codice sopra è implicitamente completato con il seguente codice:

 trait Writer { def write(str: String): Unit } object Terminal extends Writer { def write(str: String): Unit = { System.out.println(str) } }" 

Dalla specifica del linguaggio:

“Con metodi che richiedono solo un singolo parametro, Scala consente allo sviluppatore di sostituire il file .con uno spazio e di omettere le parentesi, abilitando la syntax dell’operatore mostrata nell’esempio dell’operatore di inserimento. Questa syntax viene utilizzata in altre posizioni dell’API Scala, ad esempio come build istanze Range:

 val firstTen:Range = 0 to 9 

Qui di nuovo, a (Int) è un metodo vanilla dichiarato all’interno di una class (in effetti ci sono alcune conversioni di tipo implicito in più, ma si ottiene la deriva). ”

Da Scala per Java Rifugiati, parte 6: Getting Over Java :

“Ora, quando provi” m 0 “, Scala lo considera un operatore unario, per il fatto di non essere valido (~,!, – e +). Trova che” m “è un object valido – è una funzione, non un metodo, e tutte le funzioni sono oggetti.

Poiché “0” non è un identificatore Scala valido, non può essere né un infisso né un operatore postfisso. Pertanto, Scala si lamenta che si aspettava “;” – che separerebbe due espressioni (quasi) valide: “m” e “0”. Se lo hai inserito, allora si lamenterebbe che m richiede un argomento o, in mancanza, un “_” per trasformarlo in una funzione parzialmente applicata. ”

“Credo che lo stile di syntax dell’operatore funzioni solo quando hai un object esplicito sul lato sinistro.La syntax è pensata per permetterti di esprimere le operazioni in stile” operando operatore operando “in modo naturale.”

Quali personaggi posso omettere in Scala?

Ma ciò che mi confonde è anche questa citazione:

“Deve esserci un object per ricevere una chiamata al metodo, ad esempio non è ansible eseguire” println “Hello World!” “Poiché println ha bisogno di un object destinatario. Puoi fare “Console println” Hello World! “” Che soddisfa il bisogno. ”

Perché per quanto posso vedere, c’è un object per ricevere la chiamata …

In realtà, in seconda lettura, forse questa è la chiave:

Con metodi che richiedono solo un singolo parametro, Scala consente allo sviluppatore di sostituire il. con uno spazio e omettere le parentesi

Come menzionato nel post del blog: http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 .

Quindi forse questo è in realtà uno “zucchero syntax” molto rigoroso che funziona solo dove si sta effettivamente chiamando un metodo, su un object, che prende un parametro . per esempio

 1 + 2 1.+(2) 

E nient’altro.

Questo spiegherebbe i miei esempi nella domanda.

Ma come ho detto, se qualcuno potesse indicare esattamente dove nella specifica della lingua questo è specificato, sarebbe molto apprezzato.

Ok, un bravo ragazzo (paulp_ da #scala) ha sottolineato dove nella specifica del linguaggio questa informazione è:

6.12.3: La precedenza e l’associatività degli operatori determinano il raggruppamento di parti di un’espressione come segue.

  • Se in un’espressione esistono più operazioni di base, gli operatori con precedenza più elevata si legano più strettamente degli operatori con precedenza più bassa.
  • Se ci sono operazioni infix consecutive e0 op1 e1 op2. . .opn it con gli operatori op1,. . . , opn della stessa precedenza, quindi tutti questi operatori devono avere la stessa associatività. Se tutti gli operatori sono associati a sinistra, la sequenza viene interpretata come (… e0 op1 e1) op2..). Opn en. Altrimenti, se tutti gli operatori sono di destra, la sequenza viene interpretata come e0 op1 (e1 op2 (. .Opn en).).).
  • Gli operatori Postfix hanno sempre una precedenza inferiore rispetto agli operatori infissi. Ad esempio, e1 op1 e2 op2 è sempre equivalente a (e1 op1 e2) op2.

L’operando di destra di un operatore associativo di sinistra può essere costituito da diversi argomenti racchiusi tra parentesi, ad esempio e op (e1,., En). Questa espressione viene quindi interpretata come e.op (e1,.., En).

Un’operazione binaria associativa a sinistra e1 op e2 viene interpretata come e1.op (e2). Se op è rightassociative, la stessa operazione viene interpretata come {val x = e1; e2.op (x)}, dove x è un nuovo nome.

Hmm – per me non si confonde con quello che sto vedendo o semplicemente non lo capisco;)

Non ce ne sono. È probabile che riceverai consigli in merito al fatto che la funzione abbia o meno effetti collaterali. Questo è falso. La correzione consiste nel non utilizzare gli effetti collaterali nella misura ragionevole consentita da Scala. Nella misura in cui non può, tutte le scommesse sono distriggerste. Tutte le scommesse. L’uso delle parentesi è un elemento dell’insieme “tutto” ed è superfluo. Non fornisce alcun valore una volta che tutte le scommesse sono state annullate.

Questo consiglio è essenzialmente un tentativo di un sistema di effetti che fallisce (non essere confuso con: è meno utile di altri sistemi di effetti).

Cerca di non avere effetti collaterali. Dopodiché, accetta che tutte le scommesse siano distriggerste. Nascondersi dietro una notazione sintattica di fatto di un sistema di effetti può e non può che causare danni.

Trovo più facile seguire questa regola empirica: negli spazi di espressioni si alternano metodi e parametri. Nell’esempio, (service.findAllPresentations.get.first.votes.size) must be equalTo(2) analizza come (service.findAllPresentations.get.first.votes.size).must(be)(equalTo(2)) . Si noti che le parentesi intorno al 2 hanno un’associatività superiore rispetto agli spazi. Anche i punti hanno un’associatività superiore, quindi (service.findAllPresentations.get.first.votes.size) must be.equalTo(2) come (service.findAllPresentations.get.first.votes.size).must(be.equalTo(2)) .

service findAllPresentations get first votes size must be equalTo 2 come service.findAllPresentations(get).first(votes).size(must).be(equalTo).2 .