Aracılığıyla paylaş


F# 9'daki yenilikler

F# 9, programlarınızı daha güvenli, daha dayanıklı ve performanslı hale getiren bir dizi geliştirme sunar. Bu makalede, F# açık kaynak kod deposunda geliştirilenF# 9'daki önemli değişiklikler vurgulanır.

F# 9, .NET 9'da kullanılabilir. en son .NET SDK'sını.NET indirmeleri sayfasından indirebilirsiniz.

Null değer alabilen referans türleri

F#, null'ı önlemek için tasarlanmış olsa da, C# dilinde yazılmış .NET kitaplıklarıyla etkileşim kurarken belirebilir. F# artık null'ın geçerli bir değer olabileceği başvuru türleriyle tür açısından güvenli bir şekilde ilgilenmek için bir yol sağlar.

Daha fazla ayrıntı için F# 9'da Null Atanabilir Başvuru Türleri başlıklı blog yazısına göz atın.

Aşağıda bazı örnekler verilmiştir:

// Declared type at let-binding
let notAValue: string | null = null

let isAValue: string | null = "hello world"

let isNotAValue2: string = null // gives a nullability warning

let getLength (x: string | null) = x.Length // gives a nullability warning since x is a nullable string

// Parameter to a function
let len (str: string | null) =
    match str with
    | null -> -1
    | s -> s.Length  // binds a non-null result - compiler eliminated "null" after the first clause

// Parameter to a function
let len (str: string | null) =
    let s = nullArgCheck "str" str // Returns a non-null string
    s.Length  // binds a non-null result

// Declared type at let-binding
let maybeAValue: string | null = hopefullyGetAString()

// Array type signature
let f (arr: (string | null)[]) = ()

// Generic code, note 'T must be constrained to be a reference type
let findOrNull (index: int) (list: 'T list) : 'T | null when 'T : not struct =
    match List.tryItem index list with
    | Some item -> item
    | None -> null

Ayrımcı birleşim .Is* özellikleri

Ayrımcı birleşimler artık her durum için otomatik olarak oluşturulan özelliklere sahiptir ve bir değerin belirli bir duruma ait olup olmadığını denetlemenize olanak sağlar. Örneğin, aşağıdaki tür için:

type Contact =
    | Email of address: string
    | Phone of countryCode: int * number: string

type Person = { name: string; contact: Contact }

Daha önce şöyle bir şey yazmanız gerekiyordu:

let canSendEmailTo person =
    match person.contact with
    | Email _ -> true
    | _ -> false

Şimdi bunun yerine şunları yazabilirsiniz:

let canSendEmailTo person =
    person.contact.IsEmail

Kısmi etkin desenler unit option yerine bool döndürebilir

Daha önce, kısmi etkin desenler eşleşmeyi göstermek için Some () ve aksi takdirde None döndürüyordu. Artık bool'ı geri de döndürebilirler.

Örneğin, aşağıdakiler için etkin desen:

match key with
| CaseInsensitive "foo" -> ...
| CaseInsensitive "bar" -> ...

Daha önce şöyle yazılmıştı:

let (|CaseInsensitive|_|) (pattern: string) (value: string) =
    if String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase) then
        Some ()
    else
        None

Şimdi bunun yerine şunları yazabilirsiniz:

let (|CaseInsensitive|_|) (pattern: string) (value: string) =
    String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase)

Bağımsız değişkenler sağlandığında iç özellikler yerine uzantı yöntemlerini tercih et.

Uzantı yöntemlerinin bir türün iç özellikleriyle aynı adlarla tanımlandığı bazı .NET kitaplıklarında görülen bir desenle uyumlu hale getirmek için F# artık tür denetiminde başarısız olmak yerine bu uzantı yöntemlerini çözümlemektedir.

Örnek:

type Foo() =
    member val X : int = 0 with get, set

[<Extension>]
type FooExt =
    [<Extension>]
    static member X (f: Foo, i: int) = f.X <- i; f

let f = Foo()

f.X(1) // We can now call the extension method to set the property and chain further calls

Boş gövdeli hesaplama ifadeleri

F# artık boş hesaplama ifadelerini () destekliyor ().

let xs = seq { } // Empty sequence
let html =
    div {
        p { "Some content." }
        p { } // Empty paragraph
    }

Boş bir hesaplama ifadesi yazmak, hesaplama ifadesi oluşturucusunun Zero yöntemine bir çağrıyla sonuçlanır.

Bu, daha önce kullanılabilir olan builder { () }ile karşılaştırıldığında daha doğal bir söz dizimidir.

Hash yönergelerinin dize olmayan argümanlar almasına izin verilir

Derleyici için karma yönergeler, daha önce yalnızca tırnak içine alınmış metin bağımsız değişkenlerine izin veriyordu. Artık her türlü parametreyi alabilirler.

Daha önce şunları yapmıştınız:

#nowarn "0070"
#time "on"

Şimdi şunları yazabilirsiniz:

#nowarn 0070
#time on

Bu, sonraki iki değişikliğe de bağlı olur.

REPL'de belgeleri göstermek için fsi'de genişletilmiş #help yönergesi

F# Interactive'deki #help yönergesi artık belirli bir nesne ya da işleve ilişkin belgeleri gösterir ve bu nesne veya işlevi tırnak işaretleri olmadan geçirebilirsiniz.

> #help List.map;;

Description:
Builds a new collection whose elements are the results of applying the given function
to each of the elements of the collection.

Parameters:
- mapping: The function to transform elements from the input list.
- list: The input list.
Returns:
The list of transformed elements.

Examples:
let inputs = [ "a"; "bbb"; "cc" ]

inputs |> List.map (fun x -> x.Length)
// Evaluates to [ 1; 3; 2 ]

Full name: Microsoft.FSharp.Collections.ListModule.map
Assembly: FSharp.Core.dll

Daha fazla bilgi için bkz. F# Etkileşimli blog gönderisinde #help geliştirme .

#nowarn uyarılarını devre dışı bırakmak için hata kodlarında FS ön ekini desteklemesine izin ver

Daha önce bir uyarıyı devre dışı bırakmak istediğinizde ve #nowarn "FS0057"yazdığınızda bir Invalid warning number 'FS0057'alırsınız. Uyarı numarası doğru olsa da FS ön ekine sahip olmaması gerekiyordu.

Şimdi, uyarı numaraları ön ekiyle bile kabul edildiğinden bunu anlamak için zaman harcamanız gerekmez.

Bunların tümü artık çalışır:

#nowarn 57
#nowarn 0057
#nowarn FS0057

#nowarn "57"
#nowarn "0057"
#nowarn "FS0057"

Projeniz boyunca aynı stili kullanmak iyi bir fikirdir.

Özyinelemeli olmayan işlevlerde veya 'let' ifadesiyle bağlanmış değerlerde TailCall özniteliği hakkında uyarı

F# artık [<TailCall>] özniteliğini ait olmadığı bir yere koyduğunuzda bir uyarı yayar. Kodun ne yaptığı üzerinde hiçbir etkisi olmasa da, bu kodu okuyan birinin kafasını karıştırabilir.

Örneğin, bu kullanımlar artık bir uyarı gösterecektir:

[<TailCall>]
let someNonRecFun x = x + x

[<TailCall>]
let someX = 23

[<TailCall>]
let rec someRecLetBoundValue = nameof(someRecLetBoundValue)

Öznitelik hedeflerini zorunlu kılma

Derleyici artık let değerleri, işlevler, birleşim durumu bildirimleri, örtük oluşturucular, yapılar ve sınıflar üzerinde AttributeTargets'ı doğru şekilde zorlar. Bu, Xunit testine birim bağımsız değişkenini eklemeyi unutma gibi fark edilmesi zor hataları önleyebilir.

Daha önce şunları yazabilirsiniz:

[<Fact>]
let ``this test always fails`` =
  Assert.True(false)

testleri dotnet testile çalıştırdığınızda geçerler. Test işlevi aslında bir işlev olmadığından, test çalıştırıcısı tarafından göz ardı edildi.

Artık doğru öznitelik uygulamasıyla bir error FS0842: This attribute is not valid for use on this language elementalacaksınız.

standart kütüphanesi (FSharp.Core) güncellemeleri

Koleksiyonlar için rastgele işlevler

List, Arrayve Seq modülleri rastgele örnekleme ve karıştırma için yeni işlevlere sahiptir. Bu, F# dilinin yaygın veri bilimi, makine öğrenmesi, oyun geliştirme ve rastgelelik gereken diğer senaryolarda kullanılmasını kolaylaştırır.

Tüm işlevler aşağıdaki çeşitlere sahiptir:

  • Gizli, iş parçacığı güvenliğindeki, paylaşılan Random örneğini kullanır
  • Random örneğini bağımsız değişken olarak alan bir fonksiyon
  • 0,0'a eşit veya daha büyük ve 1,0'dan küçük bir kayan değer döndürmesi gereken özelleştirilmiş bir randomizer işlevi kullanan işlev.

Dört işlev (her birinin üç çeşidi vardır) vardır: Shuffle, Choice, Choicesve Sample.

Karıştırmak

Shuffle işlevleri, her öğe rastgele karışık bir konumda olacak şekilde aynı türde ve boyutta yeni bir koleksiyon döndürür. Herhangi bir konuma varma şansı, koleksiyonun uzunluğuna eşit olarak ağırlıklandırılır.

let allPlayers = [ "Alice"; "Bob"; "Charlie"; "Dave" ]
let round1Order = allPlayers |> List.randomShuffle // [ "Charlie"; "Dave"; "Alice"; "Bob" ]

Arraylar için, yeni bir tane oluşturmak yerine var olan dizideki öğeleri karıştıran InPlace çeşitlemeleri de vardır.

Seçim

Choice işlevleri, verilen koleksiyondan tek bir rastgele öğe döndürür. Rastgele seçim, koleksiyonun boyutuna göre eşit olarak ağırlıklandırılır.

let allPlayers = [ "Alice"; "Bob"; "Charlie"; "Dave" ]
let randomPlayer = allPlayers |> List.randomChoice // "Charlie"

Seçenek

Choices işlevleri, giriş koleksiyonundan N öğelerini rastgele sırayla seçerek öğelerin birden çok kez seçilmesini sağlar.

let weather = [ "Raining"; "Sunny"; "Snowing"; "Windy" ]
let forecastForNext3Days = weather |> List.randomChoices 3 // [ "Windy"; "Snowing"; "Windy" ]

Örnek

Sample işlevleri, öğelerin birden çok kez seçilmesine izin vermeden rastgele sırada giriş koleksiyonundan N öğelerini seçer. N, koleksiyon uzunluğundan büyük olamaz.

let foods = [ "Apple"; "Banana"; "Carrot"; "Donut"; "Egg" ]
let today'sMenu = foods |> List.randomSample 3 // [ "Donut"; "Apple"; "Egg" ]

İşlevlerin ve bunların değişkenlerinin tam listesi için bkz. (RFC #1135).

CustomOperationAttribute için parametresiz oluşturucu

Bu oluşturucu, hesaplama ifadesi oluşturucusu için özel bir işlem oluşturmayı kolaylaştırır. Yöntemin adını açıkça belirtmek yerine kullanır (çoğu durumda zaten ad, yöntem adıyla eşleşir).

type FooBuilder() =
    [<CustomOperation>]  // Previously had to be [<CustomOperation("bar")>]
    member _.bar(state) = state

F# listeleri ve kümeleri için C# koleksiyon ifadesi desteği

C#'den F# listeleri ve kümeleri kullanırken, bunları başlatmak için artık koleksiyon ifadelerini kullanabilirsiniz.

Onun yerine:

FSharpSet<int> mySet = SetModule.FromArray([1, 2, 3]);

Artık şunu yazabilirsiniz:

FSharpSet<int> mySet = [ 1, 2, 3 ];

Koleksiyon ifadeleri, C# dilindeki sabit F# koleksiyonlarını kullanmayı kolaylaştırır. System.Collections.Immutable koleksiyonların sahip olmadığı yapısal eşitliklerine ihtiyacınız olduğunda F# koleksiyonlarını kullanmak isteyebilirsiniz.

Geliştirici üretkenliği geliştirmeleri

Ayrıştırıcı kurtarma

Ayrıştırıcı kurtarmada sürekli iyileştirmeler yapılmıştır. Bu, düzenlemenin ortasındayken araçların (örneğin, söz dizimi vurgulama) kodla çalışmaya devam ettiği ve bunun her an söz dizimi açısından doğru olmayabileceği anlamına gelir.

Örneğin, ayrıştırıcı artık tamamlanmamış as desenleri, nesne ifadeleri, enum durum bildirimleri, kayıt bildirimleri, karmaşık birincil kurucu desenleri, çözümlenmemiş uzun tanımlayıcılar, boş eşleşme durumları, eksik birleşim durumu alanları ve eksik birleşim durumu alan türlerinde kurtarılır.

Tanılama

Tanılamalar veya derleyicinin kodunuz hakkında neleri sevmediğini anlamak, F# ile kullanıcı deneyiminin önemli bir parçasıdır. F# 9'da bir dizi yeni veya geliştirilmiş tanılama iletisi veya daha hassas tanılama konumu vardır.

Bunlar şunlardır:

  • Nesne ifadesinde belirsiz geçersiz kılma yöntemi
  • Soyut olmayan sınıflarda kullanıldığında soyut üyeler
  • Ayırt edilmiş birlik durumuyla aynı ada sahip özellik
  • Aktif desen bağımsız değişken sayısı uyuşmazlığı
  • Yinelenen alanları olan birleşimler
  • hesaplama ifadelerinde use! ile and! kullanma

65.520'den fazla yöntemi olan sınıflar için üretilen IL'de yeni bir derleme zamanı hatası da vardır. Bu tür sınıflar CLR tarafından yüklenemez ve çalışma zamanı hatasına neden olur. (Bu kadar çok yöntem yazmayacaksınız, ancak oluşturulan kodla ilgili durumlar oldu.)

Gerçek görünürlük

F#'nin derlemeleri üretirken, özel üyeleri IL'e dahili olarak yazmasıyla ilgili bir tuhaflık vardır. Bu, InternalsVisibleToaracılığıyla bir F# projesine erişimi olan F# olmayan projelerden özel üyelere uygunsuz erişim sağlar.

Bu davranış için artık --realsig+ derleyici bayrağı aracılığıyla etkinleştirilebilen isteğe bağlı bir düzeltme vardır. Projelerinizin bu davranışa bağlı olup olmadığını görmek için çözümünüzde deneyin. .fsproj dosyalarınıza şu şekilde ekleyebilirsiniz:

<PropertyGroup>
    <RealSig>true</RealSig>
</PropertyGroup>

Performans iyileştirmeleri

İyileştirilmiş eşitlik denetimleri

Eşitlik denetimleri artık daha hızlıdır ve daha az bellek ayırır.

Mesela:

[<Struct>]
type MyId =
    val Id: int
    new id = { Id = id }

let ids = Array.init 1000 MyId
let missingId = MyId -1

// used to box 1000 times, doesn't box anymore
let _ = ids |> Array.contains missingId

2 üyeli yapıya uygulanan etkilenen dizi işlevleri için karşılaştırma sonuçları

Önce:

Yöntem Ortalama Hata Nesil 0 Tahsis edilen
DiziMevcutlarıİçeriyor 15,48 ns 0,398 ns 0.0008 48 B
Dizide Olmayan Öğeler Var 5,190,95 ns 103.533 ns 0.3891 24000 B
ArrayExistsExisting 17,97 ns 0,389 ns 0.0012 72 B
ArrayExistsNonexisting 5,316,64 ns 103.776 ns 0.3891 24024 B
MevcutDiziyiBulmayıDene 24,80 ns 0,554 ns 0.0015 96 B
DiziDenenmeyenBul 5,139,58 ns 260,949 ns 0.3891 24024 B
DiziDiziniBulVeVarOlanıDene 15,92 ns 0,526 ns 0.0015 96 B
DiziDenemeIndeksBulYok 4,349,13 ns 100,750 ns 0.3891 24024 B

Sonra:

Yöntem Anlamına gelmek Hata 0. Nesil Tahsis edilen
DiziVarolanıİçeriyor 4,865 ns 0,3452 ns - -
DiziMevcutOlmayanıİçerir 766.005 ns 15.2003 nanosaniye - -
ArrayExistsExisting 8,025 ns 0,1966 ns 0.0004 24 B
DiziMevcutAmaMevcutDeğil 834.811 ns 16.2784 ns - 24 B
ArrayTryFindExisting 16.401 ns 0,3932 ns 0.0008 48 B
DiziDenemeBulunmayan 1,140,515 ns 22.7372 ns - 24 B
DiziDenemeIndexBulVar 14,864 ns 0,3648 ns 0.0008 48 B
ArrayTryFindIndexNonexisting 990.028 ns 19.7157 ns - 24 B

Tüm ayrıntıları burada okuyabilirsiniz: F# Geliştirici Hikayeleri:9 yıllık bir performans sorununu nasıl düzelttik?

Ayrımcı birleşim yapıları için alan paylaşımı

Ayrımcı bir birleşimin birden çok durumundaki alanlar aynı ada ve türe sahipse, aynı bellek konumunu paylaşarak yapının bellek ayak izini azaltabilir. (Daha önce aynı alan adlara izin verilmediğinden ikili uyumlulukla ilgili hiçbir sorun yoktu.)

Mesela:

[<Struct>]
type MyStructDU =
    | Length of int64<meter>
    | Time of int64<second>
    | Temperature of int64<kelvin>
    | Pressure of int64<pascal>
    | Abbrev of TypeAbbreviationForInt64
    | JustPlain of int64
    | MyUnit of int64<MyUnit>

sizeof<MyStructDU> // 16 bytes

Önceki verion ile karşılaştırma (benzersiz alan adlarını kullanmanız gereken yer):

[<Struct>]
type MyStructDU =
    | Length of length: int64<meter>
    | Time of time: int64<second>
    | Temperature of temperature: int64<kelvin>
    | Pressure of pressure: int64<pascal>
    | Abbrev of abbrev: TypeAbbreviationForInt64
    | JustPlain of plain: int64
    | MyUnit of myUnit: int64<MyUnit>

sizeof<MyStructDU> // 60 bytes

İntegral aralık iyileştirmeleri

Derleyici artık daha fazla start..finish ve start..step..finish ifade örneği için iyileştirilmiş kod oluşturur. Daha önce, bunlar yalnızca türü int/int32 olduğunda ve adım sabit bir 1 veya -1olduğunda iyileştirilmiştir. Diğer integral türleri ve farklı adım değerleri verimsiz IEnumerabletabanlı bir uygulama kullandı. Şimdi, bunların hepsi iyileştirilmiştir.

Bu, döngülerde 1,25× ila 8× arasında bir hız artışı sağlar.

for … in start..finish do …

Liste/dizi ifadeleri:

[start..step..finish]

ve kavramalar:

[for n in start..finish -> f n]

Liste ve dizi kapsamlarında for x in xs -> … optimize edildi.

Bununla ilgili olarak, for x in xs -> … ile liste üretimi, listeler ve diziler için optimize edilmiştir; özellikle diziler için 10 kata kadar hız artışları ve 1/3 ile 1/4 oranında yer tasarrufu sağlanarak önemli iyileştirmeler yapılmıştır.

Araç geliştirmeleri

Visual Studio'da canlı arabellekler

Bu önceden kabul etme özelliği kapsamlı bir şekilde test edilmiştir ve artık varsayılan olarak etkindir. IDE'yi destekleyen arka plan derleyicisi artık canlı dosya arabellekleriyle çalışıyor, yani değişiklikleri uygulamak için dosyaları diske kaydetmek zorunda değilsiniz. Daha önce, bu bazı beklenmeyen davranışlara neden olabilir. (En çok, düzenlenmiş ancak kaydedilmemiş bir dosyada bulunan bir simgeyi yeniden adlandırmayı denediğinizde.)

Gereksiz parantezleri kaldırmak için çözümleyici ve kod düzeltmesi

Bazen netlik için fazladan parantezler kullanılır, ancak bazen yalnızca gürültü olur. İkinci durumda, visual studio'da bunları kaldırmak için bir kod düzeltmesi alırsınız.

Mesela:

let f (x) = x // -> let f x = x
let _ = (2 * 2) + 3 // -> let _ = 2 * 2 + 3

Visual Studio'da F# için özel görselleştirici desteği

Visual Studio'daki hata ayıklayıcısı görselleştiricisi artık F# projeleriyle çalışıyor.

hata ayıklama görselleştirici

İşlem hattının ortasında gösterilen imza araç ipuçları

Daha önce, işlem hattının ortasındaki bir işlev üzerinde zaten karmaşık bir curried parametresi (örneğin, bir lambda) uygulandığında, aşağıdaki gibi bir durumda imza yardımı sağlanmazdı. Şimdi, imza araç ipucu sonraki parametre (state) için gösterilir:

araç ipucu