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ı
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 test
ile ç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 element
alacaksınız.
standart kütüphanesi (FSharp.Core) güncellemeleri
Koleksiyonlar için rastgele işlevler
List
, Array
ve 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
, Choices
ve 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!
ileand!
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, InternalsVisibleTo
aracı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 -1
olduğunda iyileştirilmiştir. Diğer integral türleri ve farklı adım değerleri verimsiz IEnumerable
tabanlı 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.
İş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: