Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O F# 10 traz várias melhorias para a linguagem F#, a biblioteca FSharp.Core e as ferramentas. Esta versão é uma versão refinada focada na clareza, consistência e desempenho, com pequenas mas significativas melhorias que tornam o seu código diário mais legível e robusto. O F# 10 é fornecido com o .NET 10 e o Visual Studio 2026.
Você pode baixar o SDK mais recente do .NET na página de downloads do .NET.
Introdução
O F# 10 está disponível em todas as distribuições .NET Core e ferramentas do Visual Studio. Para obter mais informações, consulte Introdução ao F#.
Supressão de avisos com escopo
Agora você pode suprimir avisos em seções específicas do seu código usando a nova #warnon diretiva.
Isso combina com a diretiva existente #nowarn para lhe dar um controle preciso sobre quais avisos se aplicam onde.
Anteriormente, quando se usava #nowarn, desativava um aviso para o restante do arquivo, o que poderia ocultar problemas legítimos noutras partes do arquivo.
Vejamos um exemplo motivador:
// We know f is never called with None.
let f (Some a) = // creates warning 25, which we want to suppress
// 2000 loc, where the incomplete match warning is beneficial
Se você adicionar #nowarn 25 acima da definição de função, ele desativará FS0025 para todo o restante do arquivo.
Com o F# 10, agora você pode marcar a seção exata onde deseja que o aviso seja suprimido:
#nowarn 25
let f (Some x) = // FS0025 suppressed
#warnon 25
// FS0025 enabled again
Por outro lado, se um aviso estiver desativado globalmente (por exemplo, por meio de um sinalizador do compilador), você poderá habilitá-lo localmente com #warnon.
Esta diretiva aplicar-se-á até à correspondência #nowarn ou ao fim do ficheiro.
Observações importantes sobre compatibilidade:
Esta característica inclui várias alterações que melhoram a coerência das #nowarn/#warnon diretivas.
Estas são alterações disruptivas:
- O compilador não permite mais diretivas de aviso de várias linhas e vazias.
- O compilador não permite mais espaço em branco entre
#enowarn. - Não é possível usar cadeias de caracteres entre aspas triplas, interpoladas ou literais para números de aviso.
O comportamento do script também foi alterado.
Anteriormente, quando você adicionava uma #nowarn diretiva em qualquer lugar em um script, ela se aplicava a toda a compilação.
Agora, o comportamento em scripts corresponde ao dos arquivos .fs, aplicando-se apenas até o final do arquivo ou no correspondente #warnon.
Este recurso implementa RFC FS-1146.
Modificadores de acesso em acessadores de propriedade automática
Um padrão comum na programação orientada a objetos é criar um estado publicamente legível, mas mutável privadamente. Antes do F# 10, você precisava de sintaxe de propriedade explícita com campos de suporte (variáveis ocultas que armazenam os valores reais da propriedade) para conseguir isso, o que adicionou código repetitivo:
type Ledger() =
[<DefaultValue>] val mutable private _Balance: decimal
member this.Balance with public get() = this._Balance and private set v = this._Balance <- v
Com o F# 10, agora você pode aplicar diferentes modificadores de acesso a acessadores de propriedade individuais. Isso permite especificar diferentes níveis de acesso para o acessador (getter) e modificador (setter) de uma propriedade, tornando o padrão muito mais simples.
type Ledger() =
member val Balance = 0m with public get, private set
Você pode colocar um modificador de acesso antes do nome da propriedade (aplicando-se a ambos os acessadores) ou antes de acessadores individuais, mas não ambos simultaneamente.
Observe que esse recurso não se estende aos arquivos de assinatura (.fsi).
A assinatura correta para o Ledger exemplo acima é:
type Ledger() =
member Balance : decimal
member private Balance : decimal with set
Este recurso implementa RFC FS-1141.
Parâmetros ValueOption opcionais
Agora você pode usar uma representação baseada em ValueOption<'T> struct para parâmetros opcionais.
Quando você aplica o [<Struct>] atributo a um parâmetro opcional, o compilador usa ValueOption<'T> em vez do tipo baseado em option referência.
Isso evita alocações de heap (memória alocada na pilha gerenciada que requer coleta de lixo) para o wrapper de opção, o que é benéfico em código crítico de desempenho.
Anteriormente, o F# sempre usava o tipo heap-allocated option para parâmetros opcionais, mesmo quando o parâmetro estava ausente:
// Prior to F# 10: always uses reference option
type X() =
static member M(?x : string) =
match x with
| Some v -> printfn "Some %s" v
| None -> printfn "None"
Em F# 10, pode utilizar o atributo [<Struct>] para tirar partido do ValueOption baseado em struct.
type X() =
static member M([<Struct>] ?x : string) =
match x with
| ValueSome v -> printfn "ValueSome %s" v
| ValueNone -> printfn "ValueNone"
Isso elimina alocações de heap quando o argumento está ausente, o que é benéfico em código que é crítico para o desempenho.
Escolha esta opção baseada em struct para valores pequenos ou tipos construídos com frequência onde a pressão de alocação é importante.
Use a referência baseada padrão option quando depender de auxiliares de correspondência de padrões existentes, precisar de semântica por referência ou quando a diferença de desempenho for insignificante.
Esse recurso fortalece a paridade com outras construções de linguagem F# que já suportam ValueOption.
Suporte de tail-call em expressões computacionais
O F# 10 adiciona otimizações de tail-call para expressões de computação. Os construtores de expressões computacionais agora podem optar por essas otimizações implementando métodos especiais.
Quando o compilador traduz expressões computacionais em código F# regular (um processo chamado desugaring), ele reconhece quando uma expressão como return!, yield!, ou do! aparece em uma posição de cauda.
Se o construtor fornecer os seguintes métodos, o compilador roteia essas chamadas para pontos de entrada otimizados:
-
ReturnFromFinal- chamado para uma caudareturn!(cai de volta paraReturnFromse ausente) -
YieldFromFinal- chamado para uma caudayield!(cai de volta paraYieldFromse ausente) - Para um terminal
do!, o compilador prefereReturnFromFinal, em seguidaYieldFromFinal, antes de cair de volta para o caminho normalBind.
Esses *Final membros são opcionais e existem apenas para permitir a otimização.
Os construtores que não fornecem esses membros mantêm sua semântica existente inalterada.
Por exemplo:
coroutine {
yield! subRoutine() // tail position -> YieldFromFinal if available
}
No entanto, numa posição não caudal:
coroutine {
try
yield! subRoutine() // not tail -> normal YieldFrom
finally ()
}
Nota de compatibilidade importante:
Essa alteração pode ser quebrada se um construtor de expressões de computação já definir membros com esses nomes.
Na maioria dos casos, os construtores existentes continuam a trabalhar sem modificações quando compilados com F# 10.
Os compiladores mais antigos ignorarão os novos *Final métodos, portanto, os construtores que devem permanecer compatíveis com versões anteriores do compilador não devem assumir que o compilador invocará esses métodos.
Este recurso implementa RFC FS-1330.
Ligações digitadas em expressões computacionais sem parênteses
F# 10 remove o requisito de parênteses ao adicionar anotações de tipo a associações de expressão de computação.
Agora pode-se adicionar anotações de tipo em let!, use! e and! vinculações usando a mesma sintaxe que as vinculações comuns let.
Anteriormente, você tinha que usar parênteses para anotações de tipo:
async {
let! (a: int) = fetchA()
and! (b: int) = fetchB()
use! (d: MyDisposable) = acquireAsync()
return a + b
}
No F# 10, você pode escrever anotações de tipo sem parênteses:
async {
let! a: int = fetchA()
and! b: int = fetchB()
use! d: MyDisposable = acquireAsync()
return a + b
}
Permitir _ em use! ligações
Agora você pode usar o padrão de descarte (_) em use! associações dentro de expressões de computação.
Isso alinha o comportamento de use! com ligações regulares use .
Anteriormente, o compilador rejeitou o padrão de descarte em use! associações, forçando você a criar identificadores descartáveis:
counterDisposable {
use! _ignored = new Disposable()
// logic
}
No F# 10, você pode usar o padrão de descarte diretamente:
counterDisposable {
use! _ = new Disposable()
// logic
}
Isso esclarece a intenção ao vincular recursos assíncronos cujos valores são necessários apenas para o gerenciamento do tempo de vida.
Rejeitando módulos falsamente aninhados nos tipos
O compilador agora gera um erro quando se coloca uma declaração module recuada ao mesmo nível estrutural dentro de uma definição de tipo.
Isso reforça a validação estrutural para rejeitar a colocação enganosa de módulos dentro dos tipos.
Anteriormente, o compilador aceitava module declarações com indentação dentro de definições de tipo, mas na verdade criava esses módulos como semelhantes ao tipo, em vez de os aninhar dentro dele.
type U =
| A
| B
module M = // Silently created a sibling module, not nested
let f () = ()
Com o F# 10, esse padrão gera o erro FS0058, forçando você a esclarecer sua intenção com o posicionamento adequado do módulo:
type U =
| A
| B
module M =
let f () = ()
Aviso de preterição para omitidos seq
O compilador agora avisa sobre expressões sequenciais simples que omitem o seq construtor.
Quando você usa chaves de alcance nu como { 1..10 }, você verá um aviso de descontinuação incentivando você a usar o formulário explícito seq { ... } .
Historicamente, o F# permitia uma sintaxe de "sequence comprehension lite" em casos especiais onde você podia omitir a seq palavra-chave:
{ 1..10 } |> List.ofSeq // implicit sequence, warning FS3873 in F# 10
Em F# 10, o compilador avisa sobre esse padrão e incentiva a forma explícita:
seq { 1..10 } |> List.ofSeq
Isso é atualmente um aviso, não um erro, dando-lhe tempo para atualizar sua base de código.
Se você quiser suprimir esse aviso, use a NoWarn propriedade em seu arquivo de projeto ou #nowarn diretiva localmente e passe-lhe o número de aviso: 3873.
O formulário explícito seq melhora a clareza do código e a consistência com outras expressões computacionais.
Versões futuras do F# podem tornar isso um erro, por isso recomendamos adotar a sintaxe explícita quando você atualizar seu código.
Este recurso implementa RFC FS-1033.
Imposição de destino de atributo
O F# 10 impõe a validação de destino de atributo em todas as construções de linguagem.
O compilador agora valida que os atributos apenas são aplicados aos seus destinos pretendidos, verificando AttributeTargets em valores associados a 'let', funções, casos de união, construtores implícitos, estruturas e classes.
Anteriormente, o compilador silenciosamente permitia que você aplicasse atributos incorretamente a destinos incompatíveis.
Isso causou bugs sutis, como atributos de teste sendo ignorados quando você esqueceu () de criar uma função:
[<Fact>]
let ``this is not a function`` = // Silently ignored in F# 9, not a test!
Assert.True(false)
Em F# 10, o compilador impõe destinos de atributo e gera um aviso quando os atributos são mal aplicados:
[<Fact>]
//^^^^ - warning FS0842: This attribute cannot be applied to property, field, return value. Valid targets are: method
let ``this is not a function`` =
Assert.True(false)
Nota de compatibilidade importante:
Esta é uma alteração disruptiva que pode revelar problemas anteriormente silenciosos na sua base de código. Os erros iniciais evitam problemas de descoberta de testes e garantem que atributos como analisadores e decoradores entrem em vigor conforme pretendido.
Suporte para expressões de tarefas and!
Agora você pode aguardar várias tarefas simultaneamente usando and! em expressões de tarefas.
O uso task é uma maneira popular de trabalhar com fluxos de trabalho assíncronos em F#, especialmente quando você precisa de interoperabilidade com C#.
No entanto, até agora, não havia uma maneira concisa de aguardar várias tarefas simultaneamente em uma expressão computacional.
Talvez você tenha começado com um código que aguardava cálculos sequencialmente:
// Awaiting sequentially
task {
let! a = fetchA()
let! b = fetchB()
return combineAB a b
}
Se você quisesse alterá-lo para aguardá-los simultaneamente, normalmente usaria Task.WhenAll:
// Use explicit Task combinator to await concurrently
task {
let ta = fetchA()
let tb = fetchB()
let! results = Task.WhenAll([| ta; tb |])
return combineAB ta.Result tb.Result
}
No F# 10, você pode usar and! para uma abordagem mais idiomática:
task {
let! a = fetchA()
and! b = fetchB()
return combineAB a b
}
Isso combina a semântica da versão simultânea com a simplicidade da versão sequencial.
Este recurso implementa a sugestão de linguagem F# #1363 e é implementado como uma adição à FSharp.Core biblioteca.
A maioria dos projetos obtém a versão mais recente do FSharp.Core automaticamente do compilador, a menos que fixem explicitamente uma versão.
Nesse caso, você precisará atualizá-lo para usar esse recurso.
Melhor recorte automático por padrão
O F# 10 remove um pouco de atrito de longa data com o corte de montagens F#.
O corte é o processo de remoção de código não utilizado do seu aplicativo publicado para reduzir seu tamanho.
Você não precisa mais manter manualmente um arquivo ILLink.Substitutions.xml apenas para remover grandes blobs de recursos de metadados F# (dados de assinatura e otimização que o compilador utiliza, mas que sua aplicação não necessita durante a execução).
Quando se publica com a trimagem habilitada (PublishTrimmed=true), a compilação do F# passa a gerar automaticamente um arquivo de substituições incorporado destinado exclusivamente a estes recursos do F# apenas para ferramentas.
Anteriormente, você tinha que manter manualmente esse arquivo para remover os metadados. Isso aumentou a carga de manutenção e foi fácil de esquecer.
O resultado é uma saída menor por padrão, menos código repetitivo para manter e um risco de manutenção a menos.
Se você precisar de controle manual total, você ainda pode adicionar seu próprio arquivo de substituições.
Você pode desativar a geração automática com a <DisableILLinkSubstitutions>false</DisableILLinkSubstitutions> propriedade.
Compilação paralela em prévia
Uma atualização empolgante para usuários de F# que procuram reduzir os tempos de compilação: os recursos de compilação paralela estão se estabilizando.
A partir do .NET 10, três recursos: verificação de tipo baseada em gráfico, geração de código IL paralelo e otimização paralela são agrupados sob a propriedade do ParallelCompilation projeto.
O F# 10 ativa esta configuração por defeito para projetos que usam LangVersion=Preview.
Planejamos habilitá-lo para todos os projetos no .NET 11.
Certifique-se de experimentá-lo e ver se ele acelera sua compilação. Para habilitar a compilação paralela em F# 10:
<PropertyGroup>
<ParallelCompilation>true</ParallelCompilation>
<Deterministic>false</Deterministic> <!-- Note: deterministic builds don't get the benefits of parallel compilation -->
</PropertyGroup>
Se quiser desativar enquanto ainda desfruta de outros recursos de visualização, defina ParallelCompilation como false:
<PropertyGroup>
<LangVersion>Preview</LangVersion>
<ParallelCompilation>false</ParallelCompilation>
</PropertyGroup>
A compilação paralela pode reduzir significativamente os tempos de compilação para projetos com vários arquivos e dependências.
Cache de subsunção de tipos
O compilador agora armazena em cache verificações de relacionamento de tipo para acelerar a inferência de tipo e melhorar o desempenho do IDE, particularmente ao trabalhar com hierarquias de tipo complexas. Ao armazenar e reutilizar resultados de verificações de subsunção anteriores, o compilador evita cálculos redundantes que anteriormente atrasavam a compilação e o IntelliSense.
Gerenciando o cache:
Na maioria dos casos, o cache de subsunção de tipo melhora o desempenho sem qualquer configuração. No entanto, se você tiver um maior espaço ocupado pela memória ou maior uso da CPU (devido aos trabalhadores de manutenção de cache), poderá ajustar o comportamento do cache:
- Para desativar totalmente o cache, defina
<LangVersion>9</LangVersion>em seu arquivo de projeto para retornar ao comportamento F# 9. - Para desativar a remoção assíncrona de cache (que aumenta a pressão de thread) e usar a remoção síncrona, defina a
FSharp_CacheEvictionImmediate=1variável de ambiente.