Note
ãã®ããŒãžã«ã¢ã¯ã»ã¹ããã«ã¯ãæ¿èªãå¿ èŠã§ãã ãµã€ã³ã€ã³ãŸãã¯ãã£ã¬ã¯ããªã®å€æŽã詊ãããšãã§ããŸãã
ãã®ããŒãžã«ã¢ã¯ã»ã¹ããã«ã¯ãæ¿èªãå¿ èŠã§ãã ãã£ã¬ã¯ããªã®å€æŽã詊ãããšãã§ããŸãã
F# 11ãžããããã ãã®ãªãªãŒã¹ã«ããããã¡ã€ã³ ã¢ãã«ãæç¢ºã«åºå¥ããæ±çšã¢ã«ãŽãªãºã ãåå©çšããéåæåŠçã調æŽããã³ã³ãã€ã©ã®ã¡ã¢ãªäœ¿çšéãåæžã§ããããã«ãªããŸãã
- ã¬ã³ãŒã ã¹ãã¬ããã¯ãã¹ãã¬ãŒãž ã¢ãã«ãšã¢ããªã±ãŒã·ã§ã³ ã¢ãã«ã«åã®å ±æã匷å¶ããããšãªãããã£ãŒã«ããåå©çšããŸãã
- ãžã§ããªã㯠ã¢ã«ãŽãªãºã ã«ãããæ¡åŒµã¡ã³ããŒã«ãããæ¢åã®åã¯ã©ãããŒã䜿ããã«æäœãå ±æã§ããããã«ãªããŸãã
- .NETçµ±åã§ã¯ãããªã²ãŒãã¡ã¿ããŒã¿ãä¿æããAPIããã¥ã¡ã³ããåå©çšããŸãã
- éåæãã«ããŒãšã¿ã¹ã¯ ãã«ããŒã¯ããã£ã³ã»ã«ã API ã®å¢çãè¶ããŠäŒæ¬ããåæäœæ¥ãå¶éããŸãã
- ã³ã³ãã€ã©ãšã©ã€ãã©ãªã®æ¹åã«ãããç¹°ãè¿ãè¡ãããè§£æãæžããäžéšã®å²ãåœãŠãåé¿ããŸãã
ãããã®å€æŽã®èåŸã«ããææ¡ãå®è£ ãã¬ãã¥ãŒããã¹ãã«ã€ã㊠F# ã³ãã¥ããã£ã«æè¬ããŸãã 以äžã®ç¹éã«ã¯ãå ±åäœæè ã®ãªã³ã¯ãæ²èŒãããŠããŸãã
Note
ãããã®ãµã³ãã«ãå®è¡ããã«ã¯ãF# 11 ã³ã³ãã€ã©ãšãèšèŒãããæ©èœãåãã FSharp.Core 11 ãå¿
èŠã§ãã .NET 11.0.100 SDK ã§ã¯ãF# 11.2 ã®æ©èœã«ã¯ <LangVersion>preview</LangVersion> ãå¿
èŠã§ããèšèªèšå®ãšèšç»ããã SDK ã®ãµããŒããåç
§ããŠãã ããã runtime-async ã®ãµã³ãã«ã«ã¯è¿œå ã®ã©ã³ã¿ã€ã èŠä»¶ããããŸãã以åã®ããŒãžã§ã³ã® .NET 11 SDK ã ãã§ã¯ããã¹ãŠã®å€æŽãå«ãŸããŠããªãå¯èœæ§ããããŸãã
ã¬ã³ãŒã ã¹ãã¬ãã: å埩çãªãããã³ã°ã䌎ããªãåºå¥ãããã¢ãã«
ã¹ãã¬ãŒãž ã¢ãã«ãšã¢ããªã±ãŒã·ã§ã³ ã¢ãã«ãåºå¥ããããã«ãã¬ã³ãŒã ã¹ãã¬ããã¯å®£èšãç¹°ãè¿ããã«å ±æãã£ãŒã«ããåå©çšããŸãã ããã§ã¯ãã¢ãã«ã¯é¡§å®¢ã®è©³çްãå ±æããŸãããããŸããŸãªèå¥åã®çš®é¡ã䜿çšããŸãã
open System
type CustomerId = CustomerId of Guid
type CustomerDetails = { Name: string; PreferredLanguage: string }
type CustomerRow =
{ ...CustomerDetails
Id: string
InternalNote: string }
type Customer =
{ ...CustomerDetails
Id: CustomerId }
...CustomerDetails ã¯ãã£ãŒã«ãã®å®£èšã远å ããŸãã å¢çã§ã¯ã...row ã¯å€æãèå¥åãè§£æããéãå€ãã³ããŒããŸãã
let toCustomer (row: CustomerRow) =
match Guid.TryParse row.Id with
| true, id -> Ok ({ ...row; Id = CustomerId id }: Customer)
| false, _ -> Error "Invalid customer identifier."
éä¿¡å
ã¯å
±æã®è©³çްãä¿æããIdã眮ãæããInternalNoteãé€å€ããŸãã
Guid.TryParse (ã¹ãã¬ããã§ã¯ãããŸãã) ãèå¥åãæ€èšŒãã圢åŒãæ£ãããªãå
¥åã Error ãçæããŸãã
ããã¯ã¢ããªã±ãŒã·ã§ã³å±€ãããã³ã°ã®åé¡ã«å¯ŸåŠããŸãã å€ã®ã¹ãã¬ããã§ã¯ãååä»ãã¬ã³ãŒããšå¿åã¬ã³ãŒããçµã¿åãããããŸããåã¹ãã¬ããã§ã¯ãååä»ãã¬ã³ãŒãã®ã¿ãäœæãããŸãã åŸã®ãã£ãŒã«ããåªå ããããœãŒã¹ã¯é çªã« 1 åå®è¡ãããŸãã åæã®äŸããå ±æãããèŠçŽ ãåæã«å€æŽãé²åããããšã«äŒŽããªã¹ã¯ã«ã€ããŠã¯ãã¬ã³ãŒã ã¹ãã¬ãã ãªãã¬ãŒã¿ãŒãåç §ããŠãã ããã
ã¹ãã¬ããã®ã¬ã³ãŒããæäŸããã ãã @brianrourkeboll ããã«æè¬ããŸãã
ãžã§ããªã㯠ã¢ã«ãŽãªãºã ãæŒç®åãã¿ãã«ã®æ¡åŒµã¡ã³ããŒ
æ¢åã®åéã§ã¢ã«ãŽãªãºã ãå ±æããããã«ãF# 11.2 ã§ã¯ ãæ¡åŒµã¡ã³ããŒãéçã«è§£æ±ºãããåãã©ã¡ãŒã¿ãŒ (SRTP) å¶çŽãæºããããšãã§ããŸãã ããšãã°ã1 ã€ã®å€æã§ãã©ãããŒåã®ãªããªã¹ããšãªãã·ã§ã³ãåŠçã§ããŸãã
Note
.NET 11.0.100 SDK ã§ã¯ãæ¡åŒµåã解決ãããå Žæã§ <LangVersion>preview</LangVersion> ãæå¹ã«ãããã®ã¢ãžã¥ãŒã«ãéããŸãã
ã©ã€ãã©ãªã®èŠä»¶ãåç
§ããŠãã ããã
ãªã¹ããšãªãã·ã§ã³éã§å€æãå ±æãã
ã©ã€ãã©ãªã® map ãã¹ã³ãŒãå
ã«ããå Žåãã¢ããªã±ãŒã·ã§ã³ã¯ã©ã¡ãã®è¡šçŸãã¯ãªãŒã³ã¢ããããããèŠçŽ ã®åã倿Žãããã倿ãçµã¿åããããããããšãã§ããŸã:
let inline cleanNames values =
map (fun (name: string) -> name.Trim().ToUpperInvariant()) values
let batch: string list = cleanNames [ " Ada "; " grace " ]
let optional: string option = cleanNames (Some " linus ")
let missing: string option = cleanNames (None: string option)
let lengths: int list = map String.length batch
let nested: int option list = map (map String.length) [ Some "abc"; None ]
printfn "%A; %A; %A" batch optional missing
printfn "%A; %A" lengths nested
// ["ADA"; "GRACE"]; Some "LINUS"; None
// [3; 5]; [Some 3; None]
Structural.Adaptersã®ã¢ããã¿ãŒã¯ãæ°ããã³ã³ãããŒãé©çšããã®ã§ã¯ãªãã䜿ãæ
£ããæäœã䜿çšããŸãã
type List<'T> with
static member Map(source: 'T list, mapping: 'T -> 'U) : 'U list =
List.map mapping source
type Option<'T> with
static member Map(source: 'T option, mapping: 'T -> 'U) : 'U option =
Option.map mapping source
Structural.Algorithms ã®å
±æé¢æ°ã¯ãéçã¡ã³ã㌠Map ã®å
¥ååãåãåãããŸã:
let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
(^Input :
(static member Map: ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
^Input ãš ^Output ã¯éçã«è§£æ±ºãããåã§ãã ã³ã³ãã€ã©ã¯ãåå
·è±¡åŒã³åºãã§ãå¿
èŠãªçœ²åãæã€ã¡ã³ããŒãéžæããŸãã ãããã®æ¡åŒµæ©èœã¯ãã€ã³ã¿ãŒãã§ãŒã¹ã®å®è£
ã远å ãããã®ã§ã¯ãããŸããã
ã¢ããªã±ãŒã·ã§ã³å
šäœã¯ãäž¡æ¹ã®ã¢ãžã¥ãŒã«ãéããŸãã
æŒç®åãã³ã³ããã¹ããããã³åºåã®çš®é¡
åãã¡ã«ããºã ã§ã ã¿ãã«æŒç®åãšã³ã³ããã¹ãäŒé ãš åºåæåã®æ§ç¯ããµããŒããããŸãã ãã€ãã£ãã®ã¿ãã« ããããŒã§ã¯ãå®£èšæã ãã§ãªããæ¡åŒµé¢æ°ã®è§£æ±ºãè¡ãããåŒã³åºããµã€ãã§ããF# 11.2 ã®æ©èœãæå¹ã«ãªã£ãŠããå¿ èŠããããŸãã
å¥ã®æ©èœè¿œå ã§ãã AllowOverloadOnReturnType ã䜿çšãããšã1 ã€ã®ãã¡ã¯ã㪠ã¯ã©ã¹ã§ãæ»ãå€ã«ãã£ãŠåºå¥ããããªãŒããŒããŒãã宣èšã§ããããã«ãªããŸã:
type Inject =
[<AllowOverloadOnReturnType>]
static member Pure(value: 'T) : 'T list = [value]
[<AllowOverloadOnReturnType>]
static member Pure(value: 'T) : 'T option = Some value
let batch: string list = Inject.Pure "ready"
let optional: string option = Inject.Pure "ready"
// ["ready"] and Some "ready"
ãã® Core 11 屿§ã«ã¯ãã¬ãã¥ãŒã¯å¿ èŠãããŸããã çµææ³šéã¯ãªãŒããŒããŒããéžæããŸãããªãå ŽåãåŒã³åºãã¯ãããŸããªãŸãŸã§ãã ãæ»ãå€ã®åã§éžæããããåç §ããŠãã ããã
@gusty ã«ããæ±çšããã°ã©ãã³ã° ãµããŒãã®æ¡åŒµã«æè¬ããŸãã
.NETçµ±åãšéçºãšã¯ã¹ããªãšã³ã¹
çŽæ¥ããªã²ãŒãã¯ãã³ãã©ãŒã®å®éã®ã³ã³ãã©ã¯ããä¿æããŸã
ãã³ãã©ãŒåãšãã€ã³ã屿§ããã¬ãŒã ã¯ãŒã¯ã«å ¬éããããã«ãF# 11 ã§ã¯ãã©ãããŒã§ã¯ãªããäºææ§ã®ããååä»ã颿°ãçŽæ¥ã¿ãŒã²ããã«ããããšãã§ããŸãã ããã«ãããASP.NET Core Minimal API ã¯ãã«ãŒãããµãŒãã¹ãããããŒã®å¥çŽãèªã¿åãããšãã§ããŸã:
open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.DependencyInjection
type ShipmentStore() =
member _.Status trackingId =
if trackingId = "SHP-1042" then "Dispatched" else "Unknown"
let status
(trackingId: string)
([<FromServices>] store: ShipmentStore)
([<FromHeader(Name = "X-Customer")>] customer: string) =
$"{customer}: {trackingId} is {store.Status trackingId}"
let handler = Func<string, ShipmentStore, string, string>(status)
å®è¡å¯èœãªãããžã§ã¯ããšæ§ç¯/äºææ§ã®èŠåãåç §ããŠãã ããã ãã£ããã£ãšåŒæ°ã®é©å¿ã«ã¯ãã©ãããŒãåŒãç¶ãå¿ èŠãªå ŽåããããŸãã
çŽæ¥å§ä»»ãæäŸããã ãã @kerams ããã«æè¬ããŸãã
XML ããã¥ã¡ã³ããåå©çšãã
å
±æ APIã® åèãäžåºŠã ãèšå®ããã«ã¯ããããã shared.xml ã«èšè¿°ããŠãã ãã:
<docs>
<tracking>
<remarks>An unrecognized tracking identifier has status Unknown.</remarks>
</tracking>
</docs>
GenerateDocumentationFile ãæå¹ã«ãããšãF# 11 㯠<include> ãçæãããã¡ã³ã㌠ãšã³ããªã«å±éããŸã:
/// <summary>Find the latest status of a shipment.</summary>
/// <include file="shared.xml" path="/docs/tracking/*"/>
let status trackingId =
if trackingId = "SHP-1042" then "Dispatched" else "Unknown"
/// <summary>Find the latest statuses of several shipments.</summary>
/// <include file="shared.xml" path="/docs/tracking/*"/>
let statuses trackingIds =
trackingIds |> List.map (fun id -> id, status id)
ã€ã³ã¯ã«ãŒãå®è£
ã§ã¯ã1 ã€ã®ãã©ã°ã¡ã³ãããäž¡æ¹ã®èª¬æãæŽæ°ãããŸãã ãããšã¯å¥ã«ãããŒã« ãªãŸã«ããŒã¯ <inheritdoc/> ã®åºæ¬èšè¿°ãå
¬éããŠãããã³ã³ãã€ã©ã¯ãã®ã¿ã°ãããã¥ã¡ã³ãã®å©çšè
åãã«ãã®ãŸãŸæ®ããŠããŸãã äŸãšè§£æ±ºèŠåã«ã€ããŠã¯ã XML ããã¥ã¡ã³ããåç
§ ããŠãã ããã
@T-Gro ã FSharp.Core ã§ XML ããã¥ã¡ã³ããå ±æããŠãããããšã«æè¬ããŸãã
æé©åãããŠããªããã«ãã§ã€ã³ã©ã€ã³é¢æ°ããããã°ãã
ã€ã³ã©ã€ã³ ãã«ããŒã®åŒæ°ã調ã¹ãã«ã¯ãæé©åãããŠããªããã«ãã§ let total ã«ãã¬ãŒã¯ãã€ã³ããèšå®ããaddFee ã«ã¹ããã ã€ã³ããŸãã F# 11 ã§ã¯ã次ã®ãã«ãã§åŒã³åºãå¯èœãªã¡ãœãããåªå
ãããããã«ãªããŸããã
let inline addFee (fee: decimal) (amount: decimal) =
let total = amount + fee
total
let invoiceTotal = addFee 2m 40m
printfn "%M" invoiceTotal
// 42
æé©åããããã«ãã¯ã€ã³ã©ã€ã³åãä¿æããŸãã äžéšã®æ§æã¯ãåŒãç¶ãæ¡åŒµããå¿
èŠããããŸãã ãããã®å¶éã --always-inline å¶åŸ¡ã«ã€ããŠã¯ãã€ã³ã©ã€ã³é¢æ°ã®ãããã°ãåç
§ããŠãã ããã
éåæãšã¿ã¹ã¯ã®èª¿æŽ
F# ã¯ãŒã¯ãããŒãã¿ã¹ã¯ ããŒã¹ã® API ã«æ¥ç¶ããããã«ãFSharp.Core 11 ã§ã¯ããã£ã³ã»ã«å¯Ÿå¿ããªããžãæçããããããã³çµæå€æã远å ãããŸãã
çµæã®åéã ãã§ãªããæªåŠçã® I/O ãããŠã³ãããŸã
倧éã®ãªãŒããåŠçããããšãã¢ããªã±ãŒã·ã§ã³ãåŠçãã¹ãæªåŠçã®äœæ¥éãè¶ ããŠããŸãå¯èœæ§ããããŸãã ã¿ã¹ã¯ ããŒã¹ã®äœæ¥ããã€éå§ããããã倿ããã«ã¯ãã³ãŒãã£ããŒã¿ãŒã«ã¯ããã§ã«éå§ãããŠããã¿ã¹ã¯ã§ã¯ãªãããã¡ã¯ããªãŒãå¿ èŠãšãªããŸã:
open System.IO
open System.Threading
let readFiles (paths: seq<string>) (ct: CancellationToken) =
paths
|> Seq.map (fun path ->
fun token -> File.ReadAllTextAsync(path, token))
|> Task.parallelLimit 4 ct
æå€§ 4 ã€ã®èªã¿åããåæã«å®è¡ãããŸããçµæã¯å ¥åé åºãä¿æããŸãã ãšã©ãŒãçºçããå ŽåãæäŸãããããŒã¯ã³ãçšããŠåãéå±€ã®ãã£ã³ã»ã«ãèŠæ±ããŸãã ããã¯åæå®è¡æ°ãå¶éãããã®ã§ããã1 ç§ãããã®èŠæ±æ°ãå¶éãããã®ã§ã¯ãããŸããã ã·ãŒã±ã³ã·ã£ã«ããã³ãŠãããçµæã®ããªã¢ã³ãã«ã€ããŠã¯ã éåæé¢æ°ãšã¿ã¹ã¯é¢æ° ãåç §ããŠãã ããã
åãæ¶ããšåºã«ãªãäŸå€ãå¢çãè¶ããŠä¿æãã
ã¯ãŒã¯ãããŒãå€éšäœæ¥ãéå§ãããšããã®ããŒã¯ã³ã¯ãã®äœæ¥ãšå
±ã«ç§»åããå¿
èŠããããŸãã
Async.StartTaskImmediate ã¯ãå®è¡äžã®ã¯ãŒã¯ãããŒã®ããŒã¯ã³ãã¿ã¹ã¯ãã¡ã¯ããªã«æž¡ããå®äºãåŸ
ã¡ãŸãã
open System.IO
let tryReadFile path =
async {
try
let! contents =
Async.StartTaskImmediate(fun ct -> File.ReadAllTextAsync(path, ct))
return Some contents
with :? FileNotFoundException ->
return None
}
1 ã€ã®ã¿ã¹ã¯ã®å€±æã¯ãåºã«ãªã FileNotFoundException ãšããŠãã®ãã³ãã©ãŒã«å°éããŸãã æ¢ã«äœæãããŠããã¿ã¹ã¯ãŸãã¯å€ã¿ã¹ã¯ã®å Žåã¯ãAsync.Await ã䜿çšããŸããå€ã Async.AwaitTask ã¯äŸå€ãã©ããããåäœãä¿æããŸãã
ã¿ã¹ã¯ ããŒã¹ã®åŒã³åºãå
ã®å Žåã Task.startAsyncImmediate token workflow ã¯ãªããŒã¹ ããªããžãæäŸããŸãã æåã®éåæäžæãŸã§ãåŒã³åºãå
ã®ã¹ã¬ããã§éå§ãããŸãã
çã倿ã«ã¯ã察å¿ãããã«ããŒé¢æ°ã䜿çšããŸã
å®å
šãªèšç®åŒã䜿ããã«åäžã®çµæã倿ããã«ã¯ãMicrosoft.FSharp.Control å
ã®å¯Ÿå¿ãããã«ããŒã䜿çšããŸãã ããšãã°ããã¡ã€ã«ãèªã¿èŸŒã¿ããã®é·ããååŸããError ãšããŠéåžžã®å€±æãè¿ãå Žå:
open System.IO
open System.Threading
let fileLength (path: string) (ct: CancellationToken) =
File.ReadAllTextAsync(path, ct)
|> Task.map String.length
|> Task.catch
Report 1ãå«ããã¡ã€ã«ã¯Ok 8ãæäŸããŸããèŠã€ãããªããã¡ã€ã«ã¯ãErrorãå«ãFileNotFoundExceptionãè¿ããŸãã ãã£ã³ã»ã«ã¯åŒãç¶ãé©çšãããŸãã
Async ãš ValueTask ã«ã¯ã察å¿ãã mapãbindããšã©ãŒ ãã«ããŒãçšæãããŠããŸãã詳现ã¯ãéåæãšã¿ã¹ã¯é¢æ°ãåç
§ããŠãã ããã åå²ãè€æ°ã®çžäºã«äŸåããå€ã«ã¯ãã³ã³ãã¥ããŒã·ã§ã³åŒã䜿çšããŸãã
ValueTask ãã«ããŒã䜿çšããã«ã¯ãCore ã® netstandard2.1 ãŸã㯠net10.0 ãã«ããšã1 åã®ã¿ã®äœ¿çšãå¿
èŠã§ãã
@bartelink ã® async/task API ãžã®è²¢ç®ã«æè¬ããŸãã
éåæãšã©ãŒå ±ååŸã®åéåº
æäœãã°ã¯ãåŒã³åºãå
ããã£ããããå
容ã倿Žããããé害ãçºçããå Žæãé衚瀺ã«ãããããããšãªããæ
å ±ã远å ããå¿
èŠããããŸãã èšç®åŒãã³ãã©ãŒã§ãlogger ã await ããåŸã§ã reraise () ã䜿çšã§ããããã«ãªããŸããã
Note
èšç®åŒã®åã¹ããŒã¯ãF# 11.2 ã®æ©èœã§ãã .NET 11.0.100 SDK ã§ã<LangVersion>preview</LangVersion> ãæå¹ã«ããã
open System
open System.IO
open System.Threading.Tasks
let readReport (logFailure: exn -> Task) path =
task {
try
return! File.ReadAllTextAsync(path)
with :? IOException as error ->
do! logFailure error
return reraise ()
}
ãã°èšé²ãæ£åžžã«å®äºãããšãåãäŸå€ãšå ã®ã¹ããŒãµã€ãæ å ±ãäŒæãããŸãã ãã¬ãŒã®æ éæã«ã¯ãå ã®ãã®ãšäº€æå¯èœã§ãã ããã«ãããã©ã³ã¿ã€ã ã§ã®éåæåŠçã䌎ããã«ãéåæã®ç£èŠã«é¢ããèŠæ±ã«å¯Ÿå¿ã§ããŸãã 詳现ã¯ãèšç®åŒããã®åå®è¡ãåç §ããŠãã ããã
ãµããŒãããŠããã ãã @xperiandri ããã«æè¬ããŸãã
FSharp.Core ã¯ææ°ã® .NET äžã§ãã«ãã§ããŸã
å€ãã¿ãŒã²ãããæé€ããããšãªãææ°ã® API ãå©çšã§ããããã«ãããããFSharp.Core ã§ã¯ net10.0 ã netstandard2.0netstandard2.1 ã«å ãã ã远å ããŸãã NuGet ã¯äºææ§ã®ãããã«ããéžæããŸãã ããŒã«ã®ããã±ãŒãžãæŽæ°ãããããšã§ãã³ã³ãã€ã«æžã¿ãããžã§ã¯ããšåæ§ã«ãF# Interactive ã§ãã¿ã¹ã¯ ããŒã¹ã®éåæç Žæ£ãå¯èœã«ãªããŸããã
FSharp.Core ã¿ãŒã²ãã ãã¬ãŒã ã¯ãŒã¯ãåç
§ããŠãã ããã
ã¢ãã³ Core ã¿ãŒã²ãã£ã³ã°ãš@T-Groã«ã€ããŠã¯ãã«æè¬ããŸãã
ãã«ããŒäœæè åãã®ã©ã³ã¿ã€ã éåæåŠç
ãã«ããŒã®äœè ã¯ãã³ã³ãã€ã©ãçæããã¹ããŒããã·ã³ã䜿çšãã代ããã«ãã©ã³ã¿ã€ã ã«ããŒã¯ãããã¡ãœããã®äžæåæ¢ãšåéãè¡ãããããšãã§ããŸãã å®éšçãªã©ã€ãã©ãªå®çŸ©ã®ãã«ããŒã¯ããã®ä»çµã¿ãèšç®åŒãšããŠå ¬éããŠããŸã:
let readManifest (path: string) =
runtimeTask {
let! text = System.IO.File.ReadAllTextAsync path
return text.Length
}
Note
ãã®å®éšçãªãã«ããŒã¯ãF# 11.2 ã®æ©èœãå©çšããŠããããã®ã€ã³ã©ã€ã³å®è£
ãå®çŸ©ãŸãã¯å±éãããŸãã.NET 11.0.100 SDK ã§ã¯ããã¬ãã¥ãŒæ©èœãæå¹ã«ããŠãã ããã ãŸããCore 11 ã® net10.0 ãã«ããšåç
§èšå®ãããã³ runtime-async ãµããŒããåããã©ã³ã¿ã€ã ãå¿
èŠã§ãã ããã§æ€èšŒãããã©ã³ã¿ã€ã ãšåç
§ã®çµã¿åããã¯ã.NET 11 RC1 ã§ãã .NET 10 ã察象ã«ããã ãã§ã¯äžååã§ãã
Runtime async experiments ãåç
§ããŠãã ããã
FSharp.Core ã§ã¯ããã®ãµã³ãã«ãæ±çšçãªãã«ããŒãšããŠæäŸããŠããŸããã éåžžã® task ãš async ã³ãŒãã¯èªåçã«ããã«åãæ¿ããããã§ã¯ãªãããã§ã«ã³ã³ãã€ã«æžã¿ã®éåžžã®ã¿ã¹ã¯ãè¿ã API ãåŒã³åºãåŽã«ãšã£ãŠããã¬ãã¥ãŒã¯æ¬è³ªçã«å¿
èŠã§ã¯ãããŸããã
ã©ã³ã¿ã€ã éåæå¯Ÿå¿ã«ã€ã㊠@majocha ã«æè¬ããŸãã
Native AOT 察å¿ã®ããã¹ããš 2 次å é å
ãã€ãã£ã AOT ããµããŒããããããF# 11 ã§ã¯ãäžè¬çãªããã¹ãæäœãé åæäœããããªãã¬ã¯ã·ã§ã³ã«åºã¥ããã©ãŒãããåŠçãåçå²ãåœãŠã®èŠä»¶ãåé€ãããŸããã
åé·ãªãã©ãŒããã ã¬ã€ã€ãŒã䜿çšããã«å€ãåºåããæååããã«ããã
ãã§ã«æååãããå Žåã¯ããããæžãåºãããã ãã« "%s" 圢åŒã䜿çšããå¿
èŠã¯ãããŸããã æ°ããã³ã¢é¢æ°printãšstringã¯ãF# ã®printn倿ã«ãã£ãŠå€ãåãåããprintnã¯æ¹è¡ã远å ããŸãã
let report (processed: int) (total: decimal) (elapsed: TimeSpan) : string =
$"{processed} items, total {total:F2}, elapsed {elapsed.TotalMilliseconds:F0} ms"
printn (report 12 145.50m (TimeSpan.FromMilliseconds 250.0))
// 12 items, total 145.50, elapsed 250 ms
æååè£éã§ã¯ãããå€ãã®ã±ãŒã¹ã§çŽæ¥å€æãš String.Concat ã䜿çšãããŸãã
{total:F2} ã®ãã㪠.NET 圢åŒã®ãã¬ãŒã¹ãã«ããŒã¯ãã€ã³ããªã¢ã³ãæžåŒã䜿çšããå€ãããã¯ã¹åããŸããäžæ¹ã%A ã粟床æå®ä»ãã® printf 圢åŒã§ã¯ãåŒãç¶ã printf ã®æžåŒèšå®ãå¿
èŠã«ãªãå ŽåããããŸãã ããŒã«ã©ã€ãºãããåºåã«äœ¿çšããæç€ºçãªã«ã«ãã£ãŒãéžæããŠãã ããã
--reflectionfree ã䜿çšãããšãã¬ã³ãŒããå
±çšäœã«å¯ŸããŠã ToString åºåãçæãããããã«ãªããŸãã ã«ã«ãã£ã®äŸãšãµããŒããããŠãããã¹ã«ã€ããŠã¯ã è£éãšãã€ãã£ã AOT ã«é¢ããããŒãžãåç
§ããŠãã ããã
ããªã³ã颿°ãæäŸããŠããã @bbatsov ãããè£éæ©èœãå®è£ ããŠããã @charlesroddie ããã«æè¬ããŸãã
éåžžã®ãŒãããŒã¹ã®ã°ãªããæäœãä¿æãã
ãŒãããŒã¹ã®ã°ãªããã®å Žåãåçãªã³ãŒãå²ãåœãŠã®åŠçãåé¿ããããã ãã«ãArray2D ã¢ãžã¥ãŒã«ã眮ãæããå¿ èŠã¯ãããŸããã æŽæ°ãããã³ã¢å®è£ ã§ã¯ãäœ¿ãæ £ããåæåãšå€æããµããŒããããŸãã
let grid = Array2D.init 3 4 (fun row column -> row * 4 + column)
let doubled = grid |> Array2D.map (fun value -> value * 2)
let snapshot = Array2D.copy doubled
printn $"{snapshot.GetLength(0)} x {snapshot.GetLength(1)}; last = {snapshot[2, 3]}"
// 3 x 4; last = 22
Array2D.createãinitãããã³ rebase ã¯ãã³ã¢ ãã«ãå
šäœã§ 0 ããå§ãŸãå²ãåœãŠãã¹ã䜿çšããŸãã å¢çä¿æã®mapãmapiãããã³copyã§ã¯ãnet10.0 ãã«ãã§æ°ãã .NET ã®å²ãåœãŠ API ã䜿çšã§ããŸãã.NET Standard ãã«ãã§ã¯ããã©ãŒã«ããã¯ãç¶æãããŸãã 0 ãåºç¹ãšããªãé
åã¯éæšå¥šã§ã¯ãããŸãããããã€ãã£ããªå²ãåœãŠã«é¢ããå¶éã¯åŒãç¶ãæ®ããŸãã
å²ãåœãŠãã¹ãšæ®ãã®å¶éã«ã€ããŠã¯ã 2 次å é åãšãã€ãã£ã AOT ãåç §ããŠãã ããã ã¢ããªã±ãŒã·ã§ã³å šäœã®å ¬éèšºææ å ±ã確èªããŠãã ããã
Array2D ã®æ©èœåŒ·åã«å¯Ÿã㊠@T-Gro ã«æè¬ããŸãã
ã¡ã¢ãªã®ä¿æãå°ãªããäœæ¥ã®ç¹°ãè¿ããå°ãªããªããŸã
F# 11 ã§ã¯ãã³ã³ãã€ã©ã®ç¹°ãè¿ãè§£æããäžéšã®ã¢ããªã±ãŒã·ã§ã³ã«ããã¡ã¢ãªå²ãåœãŠãåæžãããŸãã ãããã¯ãããŸããŸãªã¯ãŒã¯ããŒãã«åœ±é¿ããŸãã
ãããžã§ã¯ãéã§åç §å ã©ã€ãã©ãªãå ±æãã
å ±æãã€ããªäŸåé¢ä¿ã®éè€ã³ããŒãé²ããããèšèªãµãŒãã¹ã¯ãæ¡ä»¶ãæºããã€ã³ããŒãæžã¿ã©ã€ãã©ãª ã°ã©ãããããžã§ã¯ãéã§å ±æã§ããŸãã
PR ã§å®è£ ãããä¿æã¡ã¢ãªã®å®éšã§ã¯ã10 åã®ãããžã§ã¯ããããªã ReSharper.FSharp ãœãªã¥ãŒã·ã§ã³ã®ã¡ã¢ãªäœ¿çšéããå ±ææ©èœãæå¹ã«ããããšã§ 1,547.2 MB ãã 1,002.2 MB ã«æžå°ããŸããã ããã¯ãã®ã¯ãŒã¯ããŒãã«ããããªã³/ãªãã®æž¬å®å€ã§ãããF# å šè¬ã«ããã 10 察 11 ã®åæžçã瀺ãããã§ã¯ãããŸããã å ±æãã€ããªåç §ã§ã¯ãªããããžã§ã¯ãåç §ã«ãã£ãŠæ¯é ããããœãªã¥ãŒã·ã§ã³ã¯ãã¯ããã«å°ããªæ¹åããèŠãããªãããšããããŸãã
ã¡ã¿ããŒã¿ã®èªã¿åããã€ã³ã©ã€ã³ç¹åããå¥ã®ææšã§ããäžæçãªã¡ã¢ãªå²ãåœãŠãåæžããŸãã ã¹ã³ãŒããšã³ã³ãããŒã«ã«ã€ããŠã¯ãããŒãªã³ã°ã®ããã©ãŒãã³ã¹ãåç §ããŠãã ããã
ã©ã€ãã©ãªã®ã€ã³ããŒãæ å ±ãå ±æããŠããã ãã @auduchinok ããã«æè¬ããŸãã
ããã©ã«ãã§ãç¬ç«ããäœæ¥ã䞊è¡ããŠã³ã³ãã€ã«ãã
䞊åã³ã³ãã€ã«ãããã©ã«ãã§æå¹ã«ãªãã確å®çãªåºåãšãœãŒã¹é ã®ã«ãŒã«ãç¶æãããããã«ãªããŸããã ãªãŒããŒããŒã ãã£ãã·ã¥ãšã¬ãŒãä»ããã¿ãŒã³ã®æ¹åã«ããããã®ä»ã®éè€äœæ¥ãåé¿ã§ããŸãã Visual Studio/FCS ã§ã¯ã倿ŽãããŠããªãããã¥ã¡ã³ãã®åæãããŠã³ãåæå®è¡ãã§ãã¯ãåæžãããŸãã詳现ã¯ããŒã«ã®ããã©ãŒãã³ã¹ãåç §ããŠãã ããã
ã³ãŒã«ããã¯ãšç©ºã®çµæãªããžã§ã¯ãã®å²ãåœãŠã«ãããªãŒããŒããããåé¿ãã
éžæãã List 颿°ãš Array 颿°ã§ãã³ãŒã«ããã¯ãã€ã³ã©ã€ã³åã§ããããã«ãªããŸããã ç¬èªã®ã€ã³ã©ã€ã³ ãã«ããŒã®å ŽåãOptimizeClosureIfNotInlined ã¯äžéæãªã«ãªãŒåãããã³ãŒã«ããã¯ãäžåºŠã ãé©å¿ãããŸã:
let inline foldItems
([<InlineIfLambda; OptimizeClosureIfNotInlined>] folder)
state
(items: 'T[]) =
let mutable total = state
for item in items do
total <- folder total item
total
ãã®ä»ã®å²ãåœãŠã¯ãã®ãŸãŸã«ããŠããããšãã§ããŸãã èŠä»¶ã«ã€ããŠã¯ãã«ãªãŒåãããã³ãŒã«ããã¯ã®æé©åãåç §ããŠãã ããã
äœæ¥ãæåããå žåçãªäŸã®äžã€ãšããŠãçµæã空ã§ããå ŽåããããŸãã FSharpLint ã®åæ©ä»ãã¯ãŒã¯ããŒãã§ã¯ãèŠåãæ€åºããªãã«ãŒã«ãç¹°ãè¿ãçµã¿åããããŠããŸããã Core ã§ãããå€ãã®æäœã«ãããŠå ±æããã空ã®é åãåå©çšãããããã«ãªããŸããã
let checkTitle (title: string) =
if String.IsNullOrWhiteSpace title then [|"A title is required."|]
else Array.empty
let warnings = ["Introduction"; "Installation"] |> List.toArray |> Array.collect checkTitle
let sharedEmpty = obj.ReferenceEquals(warnings, Array.empty<string>)
// true: no new empty result array
ãã®å€æŽã«ããããã€ãã©ã€ã³å ã®ãã¹ãŠã®å²ãåœãŠã§ã¯ãªããåé·ãªç©ºé åãªããžã§ã¯ããåé€ãããŸãã åå¥ã®ç©ºã®çµæããããããç°ãªãåç § ID ãæã€ããšãæåŸ ããªãã§ãã ããã
ã€ã³ã©ã€ã³ã®ã³ã¬ã¯ã·ã§ã³ ã³ãŒã«ããã¯ãšå ±æã®ç©ºé åã«ã€ããŠãååããã ããã@T-Gro ããã«æè¬ããŸãã
ããæ£ç¢ºãª API åŒã³åºããšæ¥åžžã§åœ¹ç«ã€äŸ¿å©æ©èœ
çæããããã©ã¡ãŒã¿ãŒã®é åºã倿Žãããå¯èœæ§ããããšãã«ååãèŠæ±ãã
çæããã API ãããã©ã¡ãŒã¿ãŒé ã®ç¡éç¥ã®å€æŽããä¿è·ãããããäœæè ã¯ååä»ãåŒæ°ã®äœ¿çšãå¿ é ã«ããããšãã§ããŸã:
Note
匷å¶ã¯ F# 11.2 ã®æ©èœã§ãã.NET 11.0.100 SDK ã§ã¯ããã¬ãã¥ãŒæ©èœãæå¹ã«ããŠãã ããã 䜿çšå¯èœãª System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttribute å®çŸ©ãå¿
èŠã§ãã ãã®å®å
šãªãµã³ãã«ã¯ãäºææ§å®çŸ©ãæ¬ èœããŠããåç
§ã«å¯ŸããŠãäºææ§å®çŸ©ãæäŸããŸãã
open System.Diagnostics.CodeAnalysis
type Query =
[<RequireNamedArguments>]
static member Execute(numerator: decimal, denominator: decimal) =
numerator / denominator
let ratio = Query.Execute(numerator = 3m, denominator = 4m)
// 0.75M; Query.Execute(3m, 4m) is rejected.
宣èšã®é åºã倿ŽããŠããååä»ãåŒã³åºãã®çµæ 0.75M ã¯ä¿æãããŸãããäœçœ®ããŒã¹ã®åŒã³åºãã¯æåŠãããŸãã
çæããã API ãŠãŒã¹ ã±ãŒã¹ã®å Žåããããã€ããŒã¯æ³šéãåºåããå¿
èŠããããŸãã
æ¢ã«ã©ãããããŠãããžã§ããªãã¯åŒæ°ãèªèãã
æ¢åã®ã¿ã¹ã¯ããã¬ãŒã³ãªçµæå€ãšããŠã©ããããªãããã«ãæ°ããæåŸã®ææ®µã®ãªãŒããŒããŒã ã«ãŒã«ã§ã¯ãã㢠Task<'T>ããã 'T ãåªå
ãããŸãã
Note
æãå ·äœçãªã¿ã€ãã¬ãŒã¯ãšãªãŒããŒããŒãã®åªå é äœã«é¢ããåäœã¯ãF# 11.2 ã®æ©èœã§ãã.NET 11.0.100 SDK ã§ã¯ããã¬ãã¥ãŒãæå¹ã«ããŠãã ããã
open System.Threading.Tasks
let pending = Task.FromResult 42
let wrapped = ValueTask<_>(pending)
let answer: int = wrapped.Result
// ValueTask<int>, not ValueTask<Task<int>>
çµæã¯ValueTask<Task<int>>ã§ãããValueTask<int>ã§ã¯ãããŸããã ã«ãŒã«ã®å¶éã«ã€ããŠã¯ãã ãªãŒããŒããŒãã®éžæã ãåç
§ããŠãã ããã
å€ããšã³ã㪠ãã€ã³ããåé€ããã«æ°ãããªãŒããŒããŒããåªå ãã
ã©ã€ãã©ãªã¯ãåŒã³åºãå
ã®ãœãŒã¹äœçœ®ãå«ããªãŒããŒããŒãã远å ãããšãã«ãæ¢åã®ãã€ããªã®å€ã蚺æã¡ãœãããä¿æããå ŽåããããŸãã
OverloadResolutionPriorityAttribute ã¯ãæ°ããã³ã³ãã€ã«ãããåŒã³åºãå
ã«å¯ŸããŠããã®èšå®ãæç€ºçã«æå®ããŸã:
open System.Runtime.CompilerServices
type Check =
static member That(condition: bool) =
if condition then "OK" else "Check failed."
[<OverloadResolutionPriority(1)>]
static member That(condition: bool, [<CallerLineNumber>] ?line: int) =
if condition then "OK" else $"Check failed at line {defaultArg line 0}."
let stock = 0
let diagnostic = Check.That(stock > 0)
// The diagnostic includes the caller's source line.
åŒã³åºãã倿ŽããªããŠããè¡æ å ±å¯Ÿå¿ã®ãªãŒããŒããŒããéžæãããŸãã ã¹ã³ãŒããšäºææ§ã®èŠåã«ã€ããŠã¯ãã ãªãŒããŒããŒãã®éžæã ãåç §ããŠãã ããã
.NET ã®æ¡ä»¶ä»ãnull 蚱容æ§ã®æ©èœã掻çšãã
ãã¡ã€ã«ãã¹ã null ã§ã¯ãªãå ŽåãPath API ã®æ»ãå€ã nullable ãšããŠæ±ãããŠããå Žåã§ãããã® API èªèº«ã®å¥çŽã«ãã£ãŠåŠå®ãããŠããã¯ãã® null ãã§ãã¯ã匷å¶ãããã¹ãã§ã¯ãããŸãããF# 11 ã¯ãæ¢åã® .NET ã® NotNullIfNotNull ãšããä¿èšŒãå©çšããŸããã€ãŸããååä»ãã®å
¥åå€ã null ã§ãªããã°ãæ»ãå€ã null ã§ã¯ãªããšããä¿èšŒã§ãã
open System.IO
let fileName: string = Path.GetFileName("reports/annual.csv")
let extension: string = Path.GetExtension(fileName)
printn (extension.ToUpperInvariant())
// .CSV
let fileNameOf (path: string | null) : string | null =
Path.GetFileName path
<Nullable>enable</Nullable> ãæå¹ã«ããŸãã Null 蚱容å
¥åã«ã¯ãåŒãç¶ã null 蚱容ã®åŠçãå¿
èŠã§ãã æ¡ä»¶ä»ãæ»ãã³ã³ãã©ã¯ããåç
§ããŠãã ããã å¶çŽã®ãªã C# T? API ã«ã€ããŠã¯ãå¥ã® Unchecked.withNull ãã«ããŒã䜿çšããŠãã ããã
æ¡ä»¶ä»ã null 蚱容ã®ãµããŒããæäŸããŠããã ããã@kerams ããã«æè¬ããŸãã
èšèªã«é¢ããå°èŠæš¡ãªæ¹å
ã¬ã³ãŒãåã .NET ã®ã³ã³ã¹ãã©ã¯ã¿ãŒæ§æã§åŒã³åºãã«ã¯ãF# 11.2 ã® ã¬ã³ãŒã ã³ã³ã¹ãã©ã¯ã¿ãŒ ã䜿çšããŸã:
type Person = { Name: string; Age: int }
let person = Person(Name = "Ada", Age = 36)
ååä»ãåŒæ°ã¯ããã£ãŒã«ãã®é åºãžã®äŸåãåé¿ããŸãã ãã®æ©èœã«ã€ããŠã¯ @charlesroddie ã«æè¬ããŸãã
æ¡ä»¶ä»ããœãŒã¹ã®å Žåã#elif 㯠#if ã°ã«ãŒãã®ãã¹ããåé¿ããŸã:
#if EU_WAREHOUSE
let dispatchTimeZone = "Europe/Paris"
#elif US_WAREHOUSE
let dispatchTimeZone = "America/New_York"
#else
let dispatchTimeZone = "UTC"
#endif
System.Console.WriteLine(dispatchTimeZone)
æåã«äžèŽãããã©ã³ããåªå
ãããŸãã ãµã³ãã« ãããžã§ã¯ã㯠EU_WAREHOUSE ãå®çŸ©ããEurope/Paris ãåºåããŸããã©ã¡ãã®ã·ã³ãã«ãå®çŸ©ãããŠããªãå Žåãåºå㯠UTC ã«ãªããŸãã
é¢é£ããããŒã«ãšçžäºéçšã®ã±ãŒã¹:
- 察話åããŒã«ãã§ãŒã³ãèå¥ããã«ã¯ã
#versionã䜿çšããŸãã - å€éšãã¹ããšãœãŒã¹ãå
±æããããã«ãF# ã§ã¯
#:è¡ã®ãã¹ã ãã£ã¬ã¯ãã£ããåãä»ããŸããããããã¯ããã±ãŒãžããã«ãã®æç€ºãšããŠè§£éãããããšã¯ãããŸããã - åé·ãªã€ã³ã¿ãŒãã§ã€ã¹å®£èšãåé¿ããã«ã¯ã 掟çã€ã³ã¿ãŒãã§ã€ã¹ã®æ¢å®ã®å®è£ ã䜿çšããŸãã
- éåžžã®ã¯ããŒãžã£å ã§ç¶æ¿ãããç¶æ ãèªã¿åãã«ã¯ãä¿è·ãããããŒã¹ ãã£ãŒã«ãã«å¯Ÿããæ°ããæ©èœã䜿çšããŸãã
ã¢ããã°ã¬ãŒãããåã«
ã¢ããã°ã¬ãŒãã®éã¯ã以äžã®éèŠãªå€æŽç¹ã«çæããŠãã ãã:
| 圱é¿å¯Ÿè±¡ã³ãŒã | ã¢ã¯ã·ã§ã³ |
|---|---|
.fsi ãã¡ã€ã«ãå«ãã©ã€ãã©ãª |
匷å¶åã®ããå¥çŽå±æ§ã眲åã«ã³ããŒããŸããçç¥ããã£ãå Žåã¯ãFS3888 ãšã©ãŒãšãªããŸãã |
| æ¡åŒµæ©èœããŒã¹ã®ã€ã³ã©ã€ã³ ã©ã€ãã©ãª | æšè«ãããã·ã°ããã£ãšãããã¥ãŒãµãŒ/ã³ã³ã·ã¥ãŒãèŠä»¶ã確èªããŸãã F# 11.2 ã®æšè«ã¯ãããäžè¬çãªå¶çŽãä¿æã§ããŸãã |
| ã€ã³ã¹ã¿ã³ã¹ ã¡ãœããã®å§ä»» | çŽæ¥æ§ç¯ã®åã«ã¬ã·ãŒããŒãæ€èšŒããŸããããã«ãããnull åä¿¡åŽã®ãšã©ãŒã®çºçãé²ãããšãã§ããŸãã |
| ããŒã«ã©ã€ãºãããè£éåºå | .NET 圢åŒã®ããŒã«ã«ã¯ãäžå€ã®ã«ã«ãã£ãŒã䜿çšããŸãã ããŒã«ã©ã€ãºãããåºåãæå³ãããŠããå Žåã¯ãæç€ºçãªã«ã«ãã£ãéžæããŸãã |
æ«å°Ÿã« do! ãä»ãã«ã¹ã¿ã ãã«ã㌠|
ãã€ã³ãã£ã³ã°ãšãŠãããã®è£å®ãæäŸããŸããæ«å°Ÿã® do!ã¯ãæçµè»¢é察象ã®ã¡ã³ããŒãéžæããŸããã |
æªå® let! |
次ã®èšç®æ¬äœã远å ããŸããäžå®å šãªãã€ã³ãã«ãããçŽæ¥çãªèšºæã¡ãã»ãŒãžã衚瀺ãããããã«ãªããŸããã |
| 以åã®èšèªèšå® | ãµããŒããããçªå·ä»ãã¢ãŒã㯠F# 8 ããéå§ãããéå³å¯ãªã€ã³ãã³ã ã¢ãŒãã¯åé€ãããŸããã èšèªããŒãžã§ã³ãšã€ã³ãã³ããåç §ããŠãã ããã |
| åæåã®ãããããšã®æŒç® | æŽæ°åãåºåºãšããåæåã䜿çšããŸããç¡å¹ãªåºåºåã¯æåŠãããŸãã |