Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Język F# 10 oferuje kilka ulepszeń języka F#, biblioteki FSharp.Core i narzędzi. Ta wersja jest aktualizacją poprawkową skoncentrowaną na przejrzystości, spójności i wydajności, z niewielkimi, ale znaczącymi ulepszeniami, dzięki którym codzienny kod jest bardziej czytelny i niezawodny. Język F# 10 jest dostarczany z platformami .NET 10 i Visual Studio 2026.
Najnowszy zestaw .NET SDK można pobrać ze strony pobierania platformy .NET.
Wprowadzenie
Język F# 10 jest dostępny we wszystkich dystrybucjach platformy .NET Core i narzędziach programu Visual Studio. Aby uzyskać więcej informacji, zobacz Wprowadzenie do języka F#.
Tłumienie ostrzeżeń w zakresie
Teraz można pominąć ostrzeżenia w określonych sekcjach kodu przy użyciu nowej #warnon dyrektywy.
Ta współpracuje z istniejącą #nowarn dyrektywą, aby zapewnić precyzyjną kontrolę nad tym, gdzie mają zastosowanie ostrzeżenia.
Wcześniej, gdy użyto polecenia #nowarn, powodowało to wyłączenie ostrzeżenia dla pozostałej części pliku, co mogło prowadzić do pominięcia uzasadnionych problemów w innym miejscu.
Przyjrzyjmy się przykładowi motywującego:
// 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
Jeśli dodasz #nowarn 25 powyżej definicji funkcji, wyłączy fs0025 dla całej reszty pliku.
W wersji F# 10 możesz teraz oznaczyć dokładną część, w której chcesz wyłączyć ostrzeżenie.
#nowarn 25
let f (Some x) = // FS0025 suppressed
#warnon 25
// FS0025 enabled again
Z drugiej strony, jeśli ostrzeżenie jest wyłączone globalnie (na przykład za pomocą flagi kompilatora), możesz włączyć je lokalnie za pomocą polecenia #warnon.
Niniejsza dyrektywa będzie stosowana aż do momentu dopasowania #nowarn lub końca pliku.
Ważne uwagi dotyczące zgodności:
Ta funkcja zawiera kilka zmian, które zwiększają spójność #nowarn/#warnon dyrektyw.
Te zmiany łamią dotychczasową zgodność:
- Kompilator nie zezwala już na wielowierszowe i puste dyrektywy ostrzegawcze.
- Kompilator nie zezwala już na białe znaki między
#inowarn. - Nie można używać ciągów trzykrotnie cytowanych, interpolowanych ani dosłownych dla numerów ostrzegawczych.
Zachowanie skryptu również uległo zmianie.
Wcześniej po dodaniu #nowarn dyrektywy w dowolnym miejscu w skrycie zastosowano ją do całej kompilacji.
Teraz jego zachowanie w skryptach odpowiada temu, co w .fs plikach, stosując się tylko do końca pliku lub odpowiadającego #warnon.
Ta funkcja implementuje RFC FS-1146.
Modyfikatory dostępu do metod dostępu do właściwości automatycznych
Typowym wzorcem programowania obiektowego jest utworzenie publicznie czytelnego, ale prywatnie modyfikowalnego stanu. Przed F# 10, potrzebna była jawna składnia właściwości z polami zapasowymi (ukrytymi zmiennymi, które przechowują rzeczywiste wartości właściwości), aby to osiągnąć, co dodawało powtarzalny kod:
type Ledger() =
[<DefaultValue>] val mutable private _Balance: decimal
member this.Balance with public get() = this._Balance and private set v = this._Balance <- v
W języku F# 10 można teraz stosować różne modyfikatory dostępu do poszczególnych akcesorów właściwości. Dzięki temu można określić różne poziomy dostępu dla gettera i settera właściwości, co czyni ten wzorzec znacznie prostszym.
type Ledger() =
member val Balance = 0m with public get, private set
Modyfikator dostępu można umieścić przed nazwą właściwości (stosującą się do obu metod dostępu) lub przed poszczególnymi metodami dostępu, ale nie jednocześnie.
Należy pamiętać, że ta funkcja nie dotyczy plików podpisu (.fsi).
Prawidłowy podpis dla powyższego przykładu Ledger to:
type Ledger() =
member Balance : decimal
member private Balance : decimal with set
Ta funkcja implementuje RFC FS-1141.
Opcjonalne parametry ValueOption
Teraz możesz użyć reprezentacji opartej na ValueOption<'T> strukturach dla parametrów opcjonalnych.
Kiedy zastosujesz atrybut [<Struct>] do opcjonalnego parametru, kompilator użyje ValueOption<'T> zamiast typu opartego na odwołaniach option.
Pozwala to uniknąć alokacji sterty (pamięci przydzielonej na zarządzaną stertę, która wymaga odzyskiwania pamięci) dla opakowania opcji, co jest korzystne w kodzie, gdzie wydajność ma kluczowe znaczenie.
Wcześniej język F# zawsze używał typu alokowanego na stercie dla parametrów opcjonalnych, nawet jeśli parametr był nieobecny.
// 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"
W języku F# 10 możesz użyć atrybutu
type X() =
static member M([<Struct>] ?x : string) =
match x with
| ValueSome v -> printfn "ValueSome %s" v
| ValueNone -> printfn "ValueNone"
Eliminuje to alokacje sterty, gdy argument jest nieobecny, co jest korzystne w kodzie krytycznym dla wydajności.
Wybierz tę opcję opartą na strukturach dla małych wartości lub często tworzonych typów, gdzie ważne jest obciążenie alokacji.
Użyj domyślnych odwołań opartych na option gdy polegasz na istniejących funkcjach pomocniczych dopasowujących wzorce, potrzebujesz semantyki odwołań, lub jeśli różnica w wydajności jest pomijalna.
Ta funkcja wzmacnia zgodność z innymi konstrukcjami języka F#, które już obsługują ValueOption.
Obsługa wywołań końcowych w wyrażeniach obliczeniowych
F# 10 dodaje optymalizacje wywołań końcowych dla wyrażeń obliczeniowych. Konstruktorzy wyrażeń obliczeniowych mogą teraz zdecydować się na te optymalizacje, implementując specjalne metody.
Gdy kompilator tłumaczy wyrażenia obliczeniowe na zwykły kod F# (proces nazywany desugaringiem), rozpoznaje, gdy wyrażenie, takie jak return!, yield!lub do! pojawia się w pozycji ogona.
Jeśli konstruktor udostępnia następujące metody, kompilator kieruje te wywołania do zoptymalizowanych punktów wejścia:
-
ReturnFromFinal- przywołanie ogonareturn!(powraca doReturnFromw przypadku braku) -
YieldFromFinal- wywołanie ogonayield!(powraca doYieldFrom, jeśli jest nieobecny) - W przypadku terminalu
do!, kompilator preferujeReturnFromFinal, następnieYieldFromFinal, a na końcu wraca do normalnej ścieżkiBind
Te *Final elementy członkowskie są opcjonalne i istnieją wyłącznie aby umożliwić optymalizację.
Konstruktory, które nie zapewniają tych elementów, zachowają istniejącą semantykę bez zmian.
Przykład:
coroutine {
yield! subRoutine() // tail position -> YieldFromFinal if available
}
Jednak w pozycji innej niż ogon:
coroutine {
try
yield! subRoutine() // not tail -> normal YieldFrom
finally ()
}
Ważna uwaga dotycząca zgodności:
Ta zmiana może być problemowa, jeśli konstruktor wyrażeń obliczeniowych już definiuje członków o tych nazwach.
W większości przypadków istniejące konstruktory nadal działają bez modyfikacji podczas kompilowania przy użyciu języka F# 10.
Starsze kompilatory ignorują nowe *Final metody, więc konstruktorzy, którzy muszą pozostać zgodni z wcześniejszymi wersjami kompilatora, nie powinni zakładać, że kompilator wywoła te metody.
Ta funkcja implementuje RFC FS-1330.
Powiązania typizowane w wyrażeniach obliczeniowych bez nawiasów
Język F# 10 eliminuje konieczność używania nawiasów przy dodawaniu adnotacji typu do wiązań w wyrażeniach obliczeniowych.
Teraz można dodawać adnotacje typu w let!, use! i and! powiązania przy użyciu tej samej składni co zwykłe let powiązania.
Wcześniej trzeba było użyć nawiasów dla adnotacji typów:
async {
let! (a: int) = fetchA()
and! (b: int) = fetchB()
use! (d: MyDisposable) = acquireAsync()
return a + b
}
W języku F# 10 można napisać adnotacje typu bez nawiasów:
async {
let! a: int = fetchA()
and! b: int = fetchB()
use! d: MyDisposable = acquireAsync()
return a + b
}
Zezwalaj _ na powiązania use!
Teraz można użyć wzorca odrzucania (_) w use! wiązaniach w wyrażeniach obliczeniowych.
Jest to zgodne z zachowaniem use! w stosunku do standardowych powiązań use.
Wcześniej kompilator odrzucał wzorzec pomijania w powiązaniach use!, co zmuszało do tworzenia tymczasowych identyfikatorów.
counterDisposable {
use! _ignored = new Disposable()
// logic
}
W języku F# 10 można bezpośrednio użyć wzorca odrzucania:
counterDisposable {
use! _ = new Disposable()
// logic
}
Wyjaśnia to intencję podczas wiązania zasobów asynchronicznych, których wartości są potrzebne wyłącznie do zarządzania czasem życia.
Odrzucanie pseudognieżdżonych modułów w typach
Kompilator zgłasza teraz błąd, gdy umieszczasz deklarację module, która jest wcięta na tym samym poziomie strukturalnym wewnątrz definicji typu.
Spowoduje to zaostrzenie weryfikacji strukturalnej w celu odrzucenia wprowadzającego w błąd umieszczania modułów w typach.
Wcześniej kompilator akceptował module deklaracje wcięte w ramach definicji typów, ale faktycznie tworzył te moduły jako elementy równorzędne z typem, a nie zagnieżdżając je w nim.
type U =
| A
| B
module M = // Silently created a sibling module, not nested
let f () = ()
W F# 10 ten wzorzec zgłasza błąd FS0058, wymuszając wyjaśnienie twoich zamiarów poprzez właściwe umiejscowienie modułu.
type U =
| A
| B
module M =
let f () = ()
Ostrzeżenie o przestarzałym elemencie dla pominiętego seq
Kompilator ostrzega teraz o wyrażeniach bez sekwencji, które pomijają konstruktora seq .
Podczas używania najprostszych nawiasów klamrowych zakresu, takich jak { 1..10 }, zostanie wyświetlone ostrzeżenie o wycofaniu zachęcające do korzystania z jawnego formatu seq { ... }.
W przeszłości F# umożliwiał użycie specjalnej składni "sequence comprehension lite", w której można było pominąć słowo kluczowe seq.
{ 1..10 } |> List.ofSeq // implicit sequence, warning FS3873 in F# 10
W języku F# 10 kompilator ostrzega o tym wzorcu i zachęca do jawnego formularza:
seq { 1..10 } |> List.ofSeq
Jest to obecnie ostrzeżenie, a nie błąd, co daje czas na zaktualizowanie bazy kodu.
Jeśli chcesz pominąć to ostrzeżenie, użyj właściwości NoWarn w pliku projektu lub dyrektywy #nowarn lokalnie i przekaż jej numer ostrzeżenia: 3873.
Formularz jawny seq zwiększa przejrzystość kodu i spójność z innymi wyrażeniami obliczeniowymi.
Przyszłe wersje języka F# mogą spowodować wystąpienie błędu, dlatego zalecamy zastosowanie jawnej składni podczas aktualizowania kodu.
Ta funkcja implementuje RFC FS-1033.
Wymuszanie docelowego atrybutu
Język F# 10 wymusza walidację docelową atrybutu we wszystkich konstrukcjach języka. Kompilator teraz sprawdza, czy atrybuty są stosowane tylko do właściwych elementów docelowych, weryfikując wartości związane z konstrukcją let, funkcje, przypadki unii, konstruktory domyślne, struktury i klasy.
Wcześniej kompilator dyskretnie zezwolił na błędne zastosowania atrybutów do niezgodnych obiektów docelowych.
Spowodowało to subtelne błędy, takie jak atrybuty testowe ignorowane, gdy zapomniano () utworzyć funkcję:
[<Fact>]
let ``this is not a function`` = // Silently ignored in F# 9, not a test!
Assert.True(false)
W F# 10 kompilator wymusza cele atrybutów i zgłasza ostrzeżenie, gdy atrybuty są nieprawidłowo stosowane.
[<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)
Ważna uwaga dotycząca zgodności:
Jest to zmiana łamiąca, która może ujawnić wcześniej ciche problemy w kodzie źródłowym. Wczesne błędy zapobiegają problemom z odkrywaniem testów i zapewniają, że atrybuty takie jak analizatory i dekoratory działają zgodnie z oczekiwaniami.
Obsługa and! wyrażeń zadań
Teraz można jednocześnie oczekiwać na wiele zadań przy użyciu and! w wyrażeniach zadań.
Używanie task to popularny sposób pracy z asynchronicznymi przepływami pracy w języku F#, szczególnie w przypadku konieczności współdziałania z językiem C#.
Jednak do tej pory nie było zwięzłego sposobu, by czekać na wiele zadań naraz w wyrażeniu obliczeniowym.
Być może rozpoczęto od kodu, który sekwencyjnie oczekiwał na obliczenia:
// Awaiting sequentially
task {
let! a = fetchA()
let! b = fetchB()
return combineAB a b
}
Jeśli chcesz zmienić ją tak, aby oczekiwała na nie jednocześnie, zazwyczaj należy użyć polecenia 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
}
W języku F# 10 można użyć and! dla bardziej idiomatycznego podejścia:
task {
let! a = fetchA()
and! b = fetchB()
return combineAB a b
}
Łączy to semantyka współbieżnej wersji z prostotą wersji sekwencyjnej.
Ta funkcja implementuje sugestię języka F# #1363 i jest implementowana jako dodatek do FSharp.Core biblioteki.
Większość projektów pobiera najnowszą wersję FSharp.Core automatycznie z kompilatora, chyba że jawnie przypią wersję.
W takim przypadku należy zaktualizować ją, aby korzystała z tej funkcji.
Lepsze przycinanie domyślnie
Język F# 10 usuwa długotrwałe problemy związane z przycinaniem bibliotek F#.
Przycinanie to proces usuwania nieużywanego kodu z opublikowanej aplikacji w celu zmniejszenia jej rozmiaru.
Nie musisz już ręcznie utrzymywać pliku ILLink.Substitutions.xml, aby usunąć duże zasoby metadanych F# (dane sygnatury i optymalizacji używane przez kompilator, ale niepotrzebne w czasie wykonywania aplikacji).
Podczas publikowania z włączonym przycinaniem (PublishTrimmed=true) kompilacja języka F# automatycznie generuje osadzony plik podstawień przeznaczony tylko dla tych zasobów języka F#.
Wcześniej trzeba było ręcznie zachować ten plik, aby usunąć metadane. To zwiększyło obciążenie związane z konserwacją i było łatwe do przeoczenia.
Wynikiem jest domyślnie mniejsza produkcja, mniej powtarzalnego kodu do utrzymania i jedno zagrożenie konserwacyjne mniej do rozwiązania.
Jeśli potrzebujesz pełnej kontroli ręcznej, nadal możesz dodać własny plik zamienników.
Możesz wyłączyć automatyczne generowanie za pomocą właściwości <DisableILLinkSubstitutions>false</DisableILLinkSubstitutions>.
Kompilacja równoległa w wersji zapoznawczej
Ekscytująca aktualizacja dla użytkowników języka F# chcących skrócić czas kompilacji: funkcje kompilacji równoległej stabilizują się.
Począwszy od platformy .NET 10, trzy funkcje: sprawdzanie typów opartych na grafach, równoległe generowanie kodu IL i optymalizacja równoległa, są grupowane razem w ramach ParallelCompilation właściwości projektu.
Język F# 10 domyślnie włącza to ustawienie dla projektów korzystających z LangVersion=Preview.
Planujemy ją włączyć dla wszystkich projektów na platformie .NET 11.
Pamiętaj, aby spróbować sprawdzić, czy przyspiesza kompilację. Aby włączyć równoległą kompilację w języku F# 10:
<PropertyGroup>
<ParallelCompilation>true</ParallelCompilation>
<Deterministic>false</Deterministic> <!-- Note: deterministic builds don't get the benefits of parallel compilation -->
</PropertyGroup>
Jeśli chcesz zrezygnować, ale nadal korzystać z innych funkcji w wersji zapoznawczej, ustaw ParallelCompilation na false.
<PropertyGroup>
<LangVersion>Preview</LangVersion>
<ParallelCompilation>false</ParallelCompilation>
</PropertyGroup>
Kompilacja równoległa może znacznie skrócić czas kompilacji projektów z wieloma plikami i zależnościami.
Pamięć podręczna subsumpcji typu
Kompilator buforuje teraz kontrole relacji typu w celu przyspieszenia wnioskowania typu i zwiększenia wydajności środowiska IDE, szczególnie podczas pracy z hierarchiami typów złożonych. Dzięki przechowywaniu i ponownym użyciu wyników z poprzednich testów podsumpcji kompilator unika nadmiarowych obliczeń, które wcześniej spowalniały kompilację i funkcję IntelliSense.
Zarządzanie pamięcią podręczną:
W większości przypadków pamięć podręczna subsumpcji typów poprawia wydajność bez dodatkowej konfiguracji. Jeśli jednak wystąpi zwiększone zużycie pamięci lub zwiększone użycie procesora (ze względu na prace konserwacyjne pamięci podręcznej), możesz dostosować zachowanie pamięci podręcznej:
- Aby całkowicie wyłączyć pamięć podręczną, skonfiguruj
<LangVersion>9</LangVersion>w pliku projektu tak, aby powrócił do ustawień działania języka F# 9. - Aby wyłączyć asynchroniczne usuwanie pamięci podręcznej (co zwiększa obciążenie wątków) i zamiast tego używać synchronicznego usuwania, ustaw zmienną
FSharp_CacheEvictionImmediate=1środowiskową.