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# . .