Respostas:
Há algumas coisas acontecendo.
Primeiro, Scala permite que pontos e parênteses sejam omitidos em muitas chamadas de método, portanto 20 secondsé equivalente a 20.seconds()*.
Segundo, uma "conversão implícita" é aplicada. Como 20é um método Inte Intnão possui secondsmétodo, o compilador procura por uma conversão implícita que recebe Inte retorna algo que possui um secondsmétodo, com a pesquisa restrita pelo escopo da sua chamada de método.
Você importou DurationInt para seu escopo. Como DurationInté uma classe implícita com um Intparâmetro, seu construtor define uma Int => DurationIntconversão implícita . DurationIntpossui um secondsmétodo, portanto, atende a todos os critérios de pesquisa. Portanto, o compilador reescreve sua chamada como new DurationInt(20).seconds**.
* Eu quero dizer isso vagamente. 20.seconds()é realmente inválido porque o secondsmétodo não possui lista de parâmetros e, portanto, os parênteses devem ser omitidos na chamada do método.
** Na verdade, isso não é verdade porque DurationInté uma classe de valor; portanto, o compilador evitará agrupar o número inteiro, se possível.
new DurationInt(20).seconds(), desde que você sabe como ele faz isso)
secondsmétodo é definido sem parênteses, portanto, chamá-lo com parênteses é um erro.
20.seconds()em Scala, apenas que o compilador está traduzindo a chamada dessa maneira. Vale ressaltar que o Scala exige que você omita parens se o método correspondente não tiver uma lista de parâmetros, como neste caso.
A "mágica" que está acontecendo lá é chamada de "conversão implícita". Você está importando as conversões implícitas e algumas delas lidam com a conversão entre Int (e Double) em Duration. É com isso que você está lidando.
import scala.concurrent.duration._resolvida, 20 secondsmas a importação do DurationConversionsTrait não é verdade? EDIT : Acabei de perceber o que eles estão realmente importando DurationInt. Acho que é porque você não pode importar o traço real? Apenas uma implementação concreta do traço?