Quero entender em um nível baixo o que aconteceria se a estrutura de dados não fosse persistente?
Vejamos um gerador de números pseudo-aleatórios com um enorme espaço de estado (como " Mersenne twister " com um estado de 2450 bytes) como uma estrutura de dados. Nós realmente não queremos usar nenhum número aleatório mais de uma vez, então parece haver poucas razões para implementar isso como uma estrutura de dados persistente e imutável. Agora vamos perguntar a si mesmos o que pode dar errado no código a seguir:
mt_gen = CreateMersenneTwisterPRNGen(seed)
integral = MonteCarloIntegral_Bulk(mt_gen) + MonteCarloIntegral_Boundary(mt_gen)
A maioria das linguagens de programação não especificar a ordem em que MonteCarloIntegral_Bulk
e MonteCarloIntegral_Boundary
será avaliado. Se os dois fizerem referência a um mt_gen mutável como argumento, o resultado desse cálculo poderá depender da plataforma. Pior ainda, pode haver plataformas em que o resultado não seja reproduzível entre diferentes execuções.
Pode-se projetar uma estrutura de dados mutável eficiente para mt_gen, de modo que qualquer intercalação da execução MonteCarloIntegral_Bulk
e MonteCarloIntegral_Boundary
dê um resultado "correto", mas uma intercalação diferente geralmente leve a um resultado "correto". Essa não reprodutibilidade torna a função correspondente "impura" e também leva a alguns outros problemas.
A não reprodutibilidade pode ser evitada aplicando uma ordem de execução sequencial fixa. Mas, nesse caso, o código pode ser organizado de tal maneira que apenas uma única referência ao mt_gen esteja disponível a qualquer momento. Em uma linguagem de programação funcional digitada, tipos de exclusividade podem ser usados para impor essa restrição, permitindo atualizações mutáveis seguras também no contexto de linguagens de programação funcionais puras. Tudo isso pode parecer bom e elegante, mas pelo menos em teoria as simulações de Monte Carlo são embaraçosamente paralelas, e nossa "solução" acabou de destruir essa propriedade. Este não é apenas um problema teórico, mas uma questão prática muito real. No entanto, precisamos modificar (a funcionalidade oferecida por) nosso gerador de números pseudo-aleatórios e a sequência de números aleatórios que produz, e nenhuma linguagem de programação pode fazer isso automaticamente por nós. (É claro que podemos usar uma biblioteca de números pseudoaleatórios diferente que já oferece a funcionalidade necessária.)
Em um nível baixo, estruturas de dados mutáveis facilmente levam à não reprodutibilidade (e, portanto, impureza), se a ordem de execução não for seqüencial e fixa. Uma estratégia imperativa típica para lidar com esses problemas é ter fases sequenciais com ordem de execução fixa, durante as quais as estruturas de dados mutáveis são alteradas, e fases paralelas com ordem de execução arbitrária, durante as quais todas as estruturas de dados mutáveis compartilhadas permanecem constantes.
Andrej Bauer levantou a questão do alias para estruturas de dados mutáveis. Curiosamente, diferentes linguagens imperativas como Fortran e C têm suposições diferentes sobre o alias permitido de argumentos de função, e a maioria dos programadores não sabe que sua linguagem possui um modelo de alias.
A imutabilidade e a semântica de valores podem ser um pouco superestimadas. O mais importante é que o sistema de tipos e a estrutura lógica (como o modelo de máquina abstrata, o modelo de alias, o modelo de simultaneidade ou o modelo de gerenciamento de memória) da sua linguagem de programação ofereça suporte suficiente para trabalhar "com segurança" com dados "eficientes" estruturas. A introdução de "mover semântica" para o C ++ 11 pode parecer um grande passo em termos de pureza e "segurança" de um ponto de vista teórico, mas na prática é o contrário. O sistema de tipos e a estrutura lógica da linguagem foram estendidos para remover grandes partes do perigo associado à nova semântica. (E mesmo que as arestas permaneçam, isso não significa que isso não poderia ser melhorado com um "melhor"
Uday Reddy levantou a questão de que a matemática nunca funcionou com objetos de dados mutáveis e que os sistemas de tipos para programas funcionais são bem desenvolvidos para objetos de dados imutáveis. Isso me lembrou a explicação de Jean-Yves Girard de que a matemática não é usada para trabalhar com objetos mutáveis, quando ele tenta motivar a lógica linear.
Pode-se perguntar como estender o sistema de tipos e a estrutura lógica das linguagens de programação funcionais para permitir trabalhar "com segurança" com estruturas de dados não-persistentes mutáveis "eficientes". Um problema aqui pode ser que a lógica clássica e as álgebras booleanas podem não ser a melhor estrutura lógica para trabalhar com estruturas de dados mutáveis. Talvez a lógica linear e os monoides comutativos possam ser mais adequados para essa tarefa? Talvez eu devesse ler o que Philip Wadler tem a dizer sobre lógica linear como sistema de tipos para linguagens de programação funcional? Porém, mesmo que a lógica linear não consiga resolver esse problema, isso não significa que o sistema de tipos e a estrutura lógica de uma linguagem de programação funcional não possam ser estendidos para permitir "seguro" e "eficiente"