F#甘やかされてしまった、またはなぜCで書きたくないのか#

C#





, , , , . Python Javascript ( ), Java , , value-, - Integer



, . — C# .







, .

, ,

, .







F#.







?



, :









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



. ? ? , - ? , .







— , , .

:









, . ? F# :









:







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








All Articles