As it should be obvious, one of the goals of
this site is to convince to take F # seriously as a universal development language.
But while the functional style is more and more penetrating into the masses, and C # has already received such functional tools as lambdas and LINQ, it seems that C # more and more comes on the heels of F #. So, oddly enough, but I began to hear more and more how to express such thoughts:
- “C # already has most of the F # toolkit, and why should I strain with the transition?”
- “There is no need to change anything. All we have to do is wait a couple of years, and C # will get enough from F #, which will provide almost all the goodies. ”
- "F # is only slightly better than C #, but not enough to really waste time moving to it."
- “F # seems to be really nice, although it scares places. But I can’t find a practical use for it in order to use C # instead. ”
I have no doubt that now, when lambdas are also added to Java, similar comments were heard in the JVM ecosystem when discussing “Scala and Closure vs. Java”.
')
So in this article I’m going to move away from F # and focus on C # (and on his example in other popular languages) to show that even with the implementation of all conceivable means of functional languages, C # programming will never be the same as on F # .
Before I begin, I want to emphasize that I do not hate C #. It just so happens that I really like C # and is one of my favorite universal languages. It evolved into a powerful language, while remaining consistent and backward compatible, which is very difficult to achieve.
But C # is not perfect. As in most major object-oriented languages, it contains design solutions that cannot be compensated for by any LINQ or lambda charms. In this article, I will show you some of the problems that are caused by such solutions, and suggest ways to improve the language that will help avoid these problems.
(Now I put on my fireproof suit. I think I will need it!)ADDITION: It seems that many readers did not understand this article at all. Let me clarify:
- I'm not saying that languages ​​with static typing are “better” than with dynamic.
- I'm not saying that functional languages ​​are "better" than object-oriented.
- I'm not saying that being able to think through code is the most important aspect of a language.
I say that:
- The inability to think through the code brings costs in which many developers do not give themselves a report.
- moreover, the possibility of “thinking through” should be only one (of many) assessment factor, when a programming language is chosen, you just should not forget about it because of the lack of cautions.
- IF you want to be able to think through your code, then the presence in your language of the properties, which I will discuss, will make it much easier.
- the fundamental concepts of object-oriented programming (the uniqueness of the objects, the focus on behavior) are not compatible with “thinking through,” and therefore it will be difficult to modify the existing OO languages ​​to add this feature.
Nothing else. Thank!
Still, what is a "reasonable" programming language?
If you are familiar with functional programmers, you often hear the phrase “think through something,” for example, “we want to think about our programs.”
What does it mean? Why use the word "think through" instead of the usual "understand"? The use of “thinking through” originates in mathematics and logic, but I'm going to use a simple and practical definition:
- “Thinking through the code” means that you can make inferences using only what you see here and now, without having to dig in other blocks of program code.
In other words, you can predict the behavior of a code fragment just by looking at it. You may need to deal with interfaces to other components, but there is no need to open them and look inside what they do. Since we, the developers, spend most of the time reading the code, this is quite an important point in programming!
Of course, there are a huge number of recommendations on how to do it in general: naming principles, formatting rules, design patterns, etc. etc. But can, by itself, your programming language help your code to be more comprehensible, more predictable? I think the answer will be yes, but still let you solve it for yourself.
Next, I will show you some code snippets. After each fragment I will ask what you think that this code does. I deliberately do not immediately write my comments so that you can get your own thoughts. After you decide, scroll down to read my opinion.
Example 1
Let's get to the next code.
- We start with the variable x , which is assigned an integer value of 2 .
- After this, DoSomething is called, to which x is passed as a parameter.
- And after that, the variable y is assigned the result of the expression x - 1 .
The question I ask is not complicated: what is the value of
y ?
var x = 2;
DoSomething(x);
// y?
var y = x - 1;
-1. ? ? , .
! JavaScript! :
function DoSomething (foo) { x = false}
var x = 2;
DoSomething(x);
var y = x - 1;
, !
DoSomething x , , ( !) .
x false 0 ( ), y -1.
? , , , .
JavaScript — . ,
. ,
, .
C# (, , ). C# , , .
, C# , JavaScript. ! .
:
1. .C# JavaScript. …
: . , . , , . : JavaScript .2
Customer .
: ?
//
var cust1 = new Customer(99, "J Smith");
var cust2 = new Customer(99, "J Smith");
// ?
cust1.Equals(cust2);
. , Customer . .
, , IEquatable, , .
? :
— , ?
— Equals?
— - , Equals?
— , GetHashCode (, , )?
( ) , ? .
:
1. .
2. .
3
, . : ?
//
var cust = new Customer(99, "J Smith");
var order = new Order(99, "J Smith");
// ?
cust.Equals(order);
! ! -, ? , . . , ? , , . ?
.
:
1. .
2. .
3. .
: , , . , . ? , . ? , , .
4
Customer. . - .
//
var cust = new Customer();
// ?
Console.WriteLine(cust.Address.Country);
:
WriteLine?
. Address null . -, , Customer.
, , , , ? , . , , .
.
:
1. .
2. .
3. .
4. . .
5
:
—
— -
— -
—
?
//
var cust = new Customer(99, "J Smith");
//
var processedCustomers = new HashSet<Customer>();
processedCustomers.Add(cust);
//
ProcessCustomer(cust);
// ? ?
processedCustomers.Contains(cust);
, ?
. .
:
— -, - , , ?
— -,
ProcessCustomer ?
, , ( - !). (, ). ?
— ,
GetHashCode, . .
Customer! ,
Customer ,
ProcessCustomer , , :
//
var cust = new Customer(99, "J Smith");
//
var processedCustomers = new HashSet<Customer>();
processedCustomers.Add(cust);
//
var changedCustomer = ProcessCustomer(cust);
// ?
processedCustomers.Contains(cust);
, ProcessCustomer
var changedCustomer = ProcessCustomer(cust);
,
ProcessCustomer - , .
ProcessCustomer , .
, , ,
ProcessCustomer .
, , ( ). , .
!.
:
1. .
2. .
3. .
4. . .
5. .:
— , ?
— , . .
— !
6
CustomerRepository.
//
var repo = new CustomerRepository();
//
var customer = repo.GetById(42);
// ?
Console.WriteLine(customer.Id);
: :
var customer = repo.GetById(42)
, customer.Id?
, .
GetById, ,
Customer. ?
, ?
repo.GetById null? ? , .
null, . ,
Customer , - , . ,
null.
, , , . . — ( , , ).
, null . ?
, , , , , :
//
var repo = new CustomerRepository();
//
// CustomerOrError
var customerOrError = repo.GetById(42);
,
customerOrError, :
//
if (customerOrError.IsCustomer)
Console.WriteLine(customerOrError.Customer.Id);
if (customerOrError.IsError)
Console.WriteLine(customerOrError.ErrorMessage);
. , , , , -. (
).
, , .
:
1. .
2. .
3. .
4. . .
5. .
6. null.
7. ., , , .. , — !
?
, , . , C#, , .
, , . -, . , , .
, , , , — , ! , , . , .
, . "
null- — , , , ."
, «» .
, F# , , :
1. . ( ).
2. .
3. .
4. . .
5. .
6. null , .
7 , , , , (-), .
, F# - . , , null, -F# . , .
, , , , F#, , . , , , . - , .
, , ( «», ,
), : , , , , , , .
—
, , , . , — , . , .
, , null, B, , null, .
- …
: ! , !, . , . , . .
, , . , , ! , ( , , IDE, ) ?
: , . ?, . . : , , , . , , , !
: . ?, , . . , , , , . , , , « C# F#, ?»
: ?-, JavaScript ! , , Smalltalk, , . , «», .
: , . ?( - ). — : ? , . , . , :

, « ». .
, ( C# Java), , « » - .
, «» , (.NET JVM).
(- «») , - . null, , .
F# Scala/Clojure — , .
, .