C#
, , , , . Python Javascript ( ), Java , , value-, - Integer
, . — C# .
, .
, ,
, .
F#.
?
, :
- , , .
- -,
Discriminated Unions
. - Computation Expressions
- SRTP ( -)
-
null
, . - type inference
null
, , Task<IEnumerable<Employee>>
. .
, POCO :
public class Employee { public Guid Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Phone { get; set; } public bool HasAccessToSomething { get; set; } public bool HasAccessToSomethinElse { get; set; } }
, , . , ?
F# :
type Employee = { Id: Guid Name: string Email: string Phone: string HasAccessToSomething: bool HasAccessToSomethingElse: bool }
. , , . C# public
{ get; set; }
. , F# null
.
, , C# , public
:
public class Employee { public Guid Id { get; } public string Name { get; } public string Email { get; } public string Phone { get; } public bool HasAccessToSomething { get; } public bool HasAccessToSomethinElse { get; } public Employee(Guid id, string name, string email, string phone, bool hasAccessToSmth, bool hasAccessToSmthElse) { Id = id; Name = name; Email = email; Phone = phone; HasAccessToSomething = hasAccessToSmth; HasAccessToSomethinElse = hasAccessToSmthElse; } }
! , 3 : .
, , / , .
F# . .
:
let employee = { Id = Guid.NewGuid() Name = "Peter" Email = "peter@gmail.com" Phone = "8(800)555-35-35" HasAccessToSomething = true HasAccessToSomethinElse = false}
, . , — . , ? :
let employee2 = { employee with Name = "Valera" }
C#? , .
, { get; }
— . ?
?
-. - , , true
. , false
. ? ? , - ? , .
— , , .
:
- , — ,
- ,
- / -,
, , .
C# , , .
, . ? F# :
- Structural Equality
- Structural Comparison
:
if employee1 = employee2 then //...
. Equals
, Object.ReferenceEquals
, .
- , , , Equals
& GetHashCode
, . , - — , . , : , HashSet<>
& SortedSet<>
, ( , , ), .
Discriminated Unions
, , . , try { i = Convert.ToInt32("4"); } catch()...
int.TryParse
.
, . ? ValidationException
. ? IndexOutOfRangeException
!
, , , - . — OutOfMemoryException
, StackOverflowException
, AccessViolationException
.. — ? ? Int32
, 2 32 . , 10000. . Int32
, , , , "" !
— . .
, , : , " , , , ". ( ), string ErrorMessage
& bool IsSuccess
. C# — , .
-,
public class Result<TResult, TError> { public bool IsOk { get; set; } public TResult Result { get; set; } public TError Error { get; set; } }
, , , . , , IsOk
, , .
F# :
type Result<'TResult, 'TError> = | Ok of 'TResult | Error of 'TError type ValidationResult<'TInput> = | Valid of 'TInput | Invalid of string list let validateAndExecute input = match validate input with // | Valid input -> Ok (execute input) // - "" | Invalid of messages -> Error messages // ,
, , , . xml doc, - , try/catch
. — , .
, . BusinessException
ApiException
, , , , , - , , , 404
403
500
. , .
F# , match
. , DU. DU , :
type UserCreationResult = | UserCreated of id:Guid | InvalidChars of errorMessage:string | AgreeToTermsRequired | EmailRequired | AlreadyExists
, . AgreeToTermsRequired
, , .
, ( ). . , , , , .
, if/else
:
let doSmth myArray index = match Array.tryItem index myArray with | Some elem -> Console.WriteLine(elem) | None -> ()
Option:
type Option<'T> = | Some of 'T | None
, , , , - . , .
expression-based .
:
- — . .
- .
( ) , .
Expression-based design , , . :
let a = if someCondition then 1 else 2
, if
, else
.
C# :
int a = 0; if(someCondition) { a = 1; } else { a = 2; }
, a
, , .
, — I/O, . . - , .
: , . , , . DI , - .
, , , 2 5, , 3-5 , , . 1-2 (?) , . , . — - . , , , . , . , , — - , . - , . . . . , . , , .
: - 1 , , , . , DI , . , , , . , , . , , , .
-. — , . , , , . , , , , . , ? , !
:
let getReport queryData = use connection = getConnection() queryData |> DataRepository.get connection // , // lifestyle' |> Report.build
, |>
, :
let gerReport queryData = use connection = getConnection() Report.build(DataRepository.get connection queryData)
C#:
public ReportModel GetReport(QueryData queryData) { using(var connection = GetConnection()) { // Report -- . F# return Report.Build(DataRepository.Get(connection, queryData)); } }
, :
let getReport qyertData = use connection = getConnection() queryData |> (DataRepository.get connection >> Report.build)
, Report.build
. . , FsCheck
, , , . , , - .
, — 1 . ? , , , , .
, . , . , , EntityFramework Dapper, .
Statically Resolved Type Parameters (SRTP)
, .
let inline square (x: ^a when ^a: (static member (*): ^a -> ^a -> ^a)) = x * x
, . , , . !
let inline GetBodyAsync x = (^a: (member GetBodyAsync: unit -> ^b) x) open System.Threading.Tasks type A() = member this.GetBodyAsync() = Task.FromResult 1 type B() = member this.GetBodyAsync() = async { return 2 } A() |> GetBodyAsync |> fun x -> x.Result // 1 B() |> GetBodyAsync |> Async.RunSynchronously // 2
, , , — ! C#.
Computation Expressions
Result
. , , Result
. , .
,
let res arg = match doJob arg with | Error e -> Error e | Ok r -> match doJob2 r with | Error e -> Error e | Ok r -> ...
type ResultBuilder() = member __.Bind(x, f) = match x with | Error e -> Error e | Ok x -> f x member __.Return x = Ok x member __.ReturnFrom x = x let result = ResultBuilder()
:
let res arg = result { let! r = doJob arg let! r2 = doJob2 r let! r3 = doJob3 r2 return r3 }
let!
Error e
. , Ok r3
.
, . DSL.
, , — task
& async
. , — Async
. F#, , cold start, Tasks API. , . :
let myTask = task { let! result = doSmthAsync() // await Task let! result2 = doSmthElseAsync(result) return result2 } let myAsync = async { let! result = doAsync() let! result2 = do2Async(result) do! do3Async(result2) return result2 } let result2 = myAsync |> Async.RunSynchronously let result2Task = myAsync |> Async.StartAsTask let result2FromTask = myTask |> Async.AwaitTask
(DTO, ) , . 1 , , - .
, F# — , . by design, , . — : , - . , , , C# .
, 7 , 130 .
, . , 1 1 . C# . — , . , , , -. , — pattern matching, , nullable reference types.
, -, , F#, -, . Pattern matching Discriminated unions & Record destruction — , , . Nullable reference types — , Option
.
, F# — , "" .
F# — .
, . Property-based (, FsCheck) , QA . - , - . , , , - - . F# . .