⬆️ ⬇️

F # spoiled me or why I don’t want to write in C # anymore

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



')

Source: https://habr.com/ru/post/428930/



All Articles