Tenho certeza de que todos estão familiarizados com métodos genéricos do formulário:
T DoSomething<T>(T item)
Essa função também é chamada de parametricamente polimórfico (PP), especificamente PP de classificação 1 .
Digamos que esse método possa ser representado usando um objeto de função do formulário:
<T> : T -> T
Ou seja, <T>
significa T -> T
que ele usa um parâmetro de tipo T
e significa que ele usa um parâmetro do tipo e retorna um valor do mesmo tipo.
Em seguida, o seguinte seria uma função PP de classificação 2:
(<T> : T -> T) -> int
A função não possui parâmetros de tipo propriamente ditos, mas assume uma função que aceita um parâmetro de tipo. Você pode continuar isso iterativamente, tornando o aninhamento cada vez mais profundo, obtendo PP de classificação cada vez mais alta.
Esse recurso é realmente raro entre linguagens de programação. Mesmo Haskell não permite isso por padrão.
É útil? Pode descrever comportamentos difíceis de descrever de outra forma?
Além disso, o que significa algo ser impredicativo ? (neste contexto)
let sdff = (g : (f : <T> (e : T) => void) => void) => {}
Respostas:
Em geral, você usa polimorfismo de classificação mais alta quando deseja que o receptor selecione o valor de um parâmetro de tipo, e não o chamador . Por exemplo:
Qualquer função
g
que eu passar para issof
deve ser capaz de me fornecerInt
um valor de algum tipo, onde a única coisa que seg
sabe sobre esse tipo é que ele possui uma instânciaShow
. Portanto, estes são kosher:Mas estes não são:
Uma aplicação particularmente útil é usar o escopo de tipos para impor o escopo de valores . Suponha que tenhamos um objeto do tipo
Action<T>
, representando uma ação que podemos executar para produzir um resultado do tipoT
, como um futuro ou retorno de chamada.Agora, suponha que também tenhamos um
Action
que possa alocarResource<T>
objetos:Queremos reforçar que esses recursos sejam usados apenas dentro do
Action
local em que foram criados e não compartilhados entre ações diferentes ou execuções diferentes da mesma ação, para que as ações sejam determinísticas e repetíveis.Podemos usar tipos de classificação mais alta para fazer isso adicionando um parâmetro
S
aos tiposResource
eAction
, que é totalmente abstrato - ele representa o "escopo" doAction
. Agora, nossas assinaturas são:Agora, quando damos
runAction
umAction<S, T>
, temos a certeza de que, como o parâmetro "scope"S
é totalmente polimórfico, ele não pode escapar do corpo derunAction
- então qualquer valor de um tipo que useS
como oResource<S, int>
mesmo não pode escapar!(Em Haskell, isso é conhecido como
ST
mônada, onderunAction
é chamadarunST
,Resource
é chamadaSTRef
enewResource
é chamadanewSTRef
).fonte
ST
mônada é um exemplo muito interessante. Você pode dar mais alguns exemplos de quando o polimorfismo de alto escalão seria útil?data Fetch d = forall a. Fetch (d a) (MVar a)
, que é um par de uma solicitação para uma fonte de dadosd
e um slot para armazenar o resultado. O resultado e o slot devem ter tipos correspondentes, mas esse tipo está oculto, para que você possa ter uma lista heterogênea de solicitações para a mesma fonte de dados. Agora você pode usar de maior grau de polimorfismo para escrever uma função que recupera todos os pedidos, dada uma função que obtém uma:fetch :: (forall a. d a -> IO a) -> [Fetch d] -> IO ()
.Polimorfismo de classificação mais alta é extremamente útil. No Sistema F (o idioma principal das linguagens FP digitadas com as quais você está familiarizado), isso é essencial para admitir "codificações da Igreja digitadas", que é realmente como o Sistema F faz a programação. Sem eles, o sistema F é completamente inútil.
No Sistema F, definimos números como
A adição tem o tipo
que é um tipo de classificação mais alta (
forall c.
aparece dentro dessas setas).Isso aparece em outros lugares também. Por exemplo, se você deseja indicar que um cálculo é um estilo de passagem de continuação adequado (google "codensity haskell"), então você faria isso como
Mesmo falando sobre um tipo desabitado no Sistema F requer polimorfismo de classificação mais alta
O longo e curto disso, escrever uma função em um sistema de tipo puro (Sistema F, CoC) requer polimorfismo de classificação mais alta se quisermos lidar com dados interessantes.
No sistema F, em particular, essas codificações precisam ser "impredicativas". Isso significa que um
forall a.
quantifica absolutamente todos os tipos . Isso inclui criticamente o tipo que estamos definindo. Noforall a. a
quea
poderia realmente estar paraforall a. a
novamente! Em linguagens como ML, esse não é o caso, eles são considerados "predicativos", uma vez que uma variável de tipo quantifica apenas o conjunto de tipos sem quantificadores (chamados monotipos). Nossa definição deplus
impredicatividade requerida também porque instanciamos oc
inl : Nat
to beNat
!Finalmente, gostaria de mencionar uma última razão pela qual você gostaria tanto de impredicatividade quanto de polimorfismo de classificação mais alta, mesmo em um idioma com tipos arbitrariamente recursivos (ao contrário do Sistema F). Em Haskell, há uma mônada para efeitos chamada "mônada de thread de estado". A idéia é que a mônada de encadeamento de estado permita que você mude as coisas, mas exija que ela escape para que seu resultado não dependa de nada mutável. Isso significa que os cálculos de ST são notavelmente puros. Para impor esse requisito, usamos polimorfismo de classificação mais alta
Aqui, garantindo que isso
a
esteja fora do escopo em que apresentamoss
, sabemos que issoa
significa um tipo bem formado, no qual não se baseias
. Usamoss
para parameritizar todas as coisas mutáveis nesse segmento de estado específico, para que saibamos quea
é independente das coisas mutáveis e, portanto, que nada escapa ao escopo desseST
cálculo! Um exemplo maravilhoso de uso de tipos para descartar programas mal formados.A propósito, se você estiver interessado em aprender sobre a teoria dos tipos, sugiro investir em um bom livro ou dois. É difícil aprender essas coisas em pedaços. Eu sugeriria um dos livros de Pierce ou Harper sobre a teoria da PL em geral (e alguns elementos da teoria dos tipos). O livro "Tópicos avançados em tipos e linguagens de programação" também abrange uma boa quantidade de teoria de tipos. Finalmente, "Programar na teoria dos tipos de Martin Lof" é uma exposição muito boa da teoria dos tipos intensional que Martin Lof delineou.
fonte