|
@Rick_Bishop @Marc-Clifton
Hi Rick,
If you do get into the "functional programming" thing, as in F#, you will have reasons, imho, to think about the issue of "immutability" ... F# enforces that strictly.
Marc Clifton's recent articles here that demonstrate FP approaches with C# are very interesting.
I am inclined, now, to do without 'ref and 'out, and make my classes as immutable as possible using 'get only Properties, etc. You can see this in the second extension method above named 'Clone.
With a very complex Class, copying all the field/property values into a new object might be very expensive; in that case, is the Class "more immutable" if there is a specific method that modifies one or more class instance members values ? Not yet sure on that one.
In any case, I think it imperative to think carefully about when, how, and where, any Class instance may be modified.
cheers, Bill
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
BillWoodruff wrote: I think it imperative to think carefully about when, how, and where, any Class instance may be modified.
How very functional of you.
BillWoodruff wrote: I am inclined, now, to do without 'ref and 'out,
C# 7's tuple return all but eliminates the need for out. Of all the new additions to the language, it is currently my favorite.
BillWoodruff wrote: to think about the issue of "immutability" ... F# enforces that strictly.
Actually not quite, the mutable keyword explicitly declares that the field can be changed, which is particularly useful dealing with .NET classes in F#.
BillWoodruff wrote: With a very complex Class,
The interesting thing I found about F# is that it one usually doesn't write complex classes. Furthermore, a function call rarely needs to clone an object or mutate a field, instead, the function returns a completely new type with the result. This is probably the most dramatic change in thinking when using FP languages -- a function operates on a type and returns a different type with the result of the operation.
A simple example is a type that contains a vehicle make, model, and year. In C#, we might implement the class with a Bitmap for a stock image of that vehicle and method called GetStockImage() that mutates the container class by assigning the bitmap.
In F#, you might simply return a different type containing the image and let the caller combine the make/model/year with the image into a new type. Or, the call to GetStockImage could do that, returning that new type.
So in C#, you would have implemented one "type" as a class. In F#, you would have implemented two, possible three types. The beauty of the F# approach is that the function ShowStockImage is guaranteed (well, in theory) of being passed only a type instance that actually has an image that's been initialized. No "if (image == null) throw Exception" necessary!
Ideally, in F#, you would also have real types for make, model, and year, not just strings and ints!
I hope that made sense!
Latest Article - Code Review - What You Can Learn From a Single Line of Code
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
Hi Marc, That makes a whole lotta sense, as your thoughts usually do
I also, am enjoying C#'s new Tuple facility.
I can't quite see how F# would, inherently, remove a need to have complex POCOS: if a POCO gotta have a lotta fields or properties ... ? Perhaps you are suggesting using Tuples with multiple Types like a property-bag ?
The mind boggles at thhe thought of this:
public class POCOLocoTuple
{
public POCOLocoTuple()
{
}
public POCOLocoTuple(string name, int age)
{
PBag = (Guid.NewGuid(), name, age);
}
private (Guid Id, string Name, int Age) PBag { set; get; }
public string Name
{
set
{
PBag = (PBag.Id, value, PBag.Age);
}
}
public int Age
{
set
{
PBag = (PBag.Id, PBag.Name, value);
}
}
public POCOLocoTuple Clone(bool makenewguid = true)
{
Guid guid = makenewguid ? Guid.NewGuid() : PBag.Id;
POCOLocoTuple newpocoloco = new POCOLocoTuple();
newpocoloco.PBag = (guid, PBag.Name, PBag.Age);
return newpocoloco;
}
} cheers, Bill
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
modified 6-Jan-18 16:48pm.
|
|
|
|
|
BillWoodruff wrote: Perhaps you are suggesting using Tuples with multiple Types like a property-bag ?
Not really, but keep in mind that my experience with FP is limited.
BillWoodruff wrote: I can't quite see how F# would, inherently, remove a need to have complex POCOS: if a POCO gotta have a lotta fields or properties
One of the differences between imperative and functional programming is the concept of currying and partial application. Currying - you call a function with only the first subset of parameters, partial application - you create a function that itself calls a function requiring additional parameters. In either case, what you get back from currying is a function (you can think of it as "waiting for the rest of the parameters") or with partial application, you've defined a function that is "waiting for parameters").
Given that capability, the need for POCO with lots of properties (or fields in FP parlance) often becomes unnecessary. A simple example:
class BirthDate
{
public int Month {get;set;}
public int Day {get;set;}
public int Year {get;set;}
}
class Horoscope
{
public string GetHoroscope(BirthDate date);
public string GetHoroscope(int y, int m, int d);
}
Here, I've used BirthDate as a container for 3 fields, or without the container, as three parameters. In either case, calling GetHoroscope requires that all three parameters are known before making the call, so they're wrapped in a container or perhaps in local variables.
In F#, you don't need that. You can do this:
let GetHoroscope y m d = [implementation details]
Let's say you have a simple text-based UI (pseudo-codish):
let curried1 = GetHoroscope PromptForYear
let curried2 = curried1 PromptForMonth
let text = curried2 PromptForDay
Notice a few things here. First of all, for y, m, and d parameters, I'm passing in a function "PromptFor...", not an integer variable.
Secondly, curried1 and curried2 are functions expecting day and year, and year, respectively.
Where are the variables, fields, properties? They exist ONLY in [implementation details] -- the caller never creates a variable, never creates a container, etc.
So, one of the reasons POCO with lots of fields is not necessary is that those fields, in FP, are actually implemented as functions in FP, called as needed. While the example can be written imperatively:
GetHoroscope(PromptForYear(), PromptForMonth(), PromptForDay());
The problem here, with the imperative code, is that "how" you're getting those values is hard-coded -- there's no easy way to curry the C# method GetHoroscope . Even more interestingly, curried1 and curried2 can be returned as functions!
Currying, and partial function application, in C#, becomes a mess.
So one has to get out of the mindset of thinking of data, not as values but instead data as functions. That is NOT easy to do!
Latest Article - Code Review - What You Can Learn From a Single Line of Code
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
Hi Marc, Thanks for this virtual banquet of food for thought !
Clearly, I am yet unclear on the "deeper state" of the FP paradigm, but, it is very interesting.
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
Hi @BillWoodruff, Funny you'd mention immutability. I've been reading about immutable properties and started using them in my project. These are the properties with only getters and no setters, that can only be set from the constructor. It turns out that many of my objects are like that because I tend to use a lot of classes just to hold data rather than having to make additional database calls, especially inside a loop. I hope this is the correct usage. If not, I've got a lot of searching and replacing to do.
The lack of surety in programming is part of the reason software is fragile.
|
|
|
|
|
Except for method chaining, why would pass the same object back to the caller? Since the object is passed by reference, the caller's reference is updated. So, in your example:
SomeObj myobj = new SomeObj ( someparam = "Some Value" );
myobj = DoSomething( myobj );
the assignment is unnecessary and potentially confusing -- the reader of the code might wonder if myobj is not the same instance that is returned from the call DoSomething in which you passed in myobj
Of course, if SomeObj is struct, then it's passed by value, and yeah, you have to return the "value" for the caller to see the change.
I must say, I'm amused by all this because your question hit the bullseye of how messy method calling can be.
As a digression, think of higher level method as a workflow:
var stuffWorkflowNeedsToKnow = InitializeStuff();
DoStep1(stuffWorkflowNeedsToKnow);
DoStep2(stuffWorkflowNeedsToKnow);
DoStep3(stuffWorkflowNeedsToKnow);
And a further digression: Class-less Coding - Minimalist C# and Why F# and Function Programming Has Some Advantages
Latest Article - Code Review - What You Can Learn From a Single Line of Code
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
Are there any C# programmer forums that are more forgiving towards people who are fairly new to the language?
Where it's ok to ask questions that haven't been thoroughly researched?
Where you don't get downvoted because a question has been asked before (because every question has)?
A resource where one might even get assistance with asking the question itself?
Often times, questions are difficult to articulate due to the learner's lack of experience and unfamiliarity with the vocabulary.
I can continue doing this on my own, but the learning curve could really improve if I could ask a few questions that might seem ridiculously easy to an experienced programmer. Entire technologies has been deprecated since C# was invented, yet it's all still online as recommended best practices.
|
|
|
|
|
I would say that you can ask every question in this Forum.
Of course you often would get a Link which could answer your question.
My experience with this forum is : as better you specify your issue as better the answer would be - independant if it's posted elsewhere
What you should avoid is any question which could understood as "please write the code for me" or "I don't want to use the debugger" or "what the hell is Google" or could be interpreted as homework from school ...
|
|
|
|
|
|
If only I knew someone who'd written such an article[^]. It's over 10 years old and not a lot has changed in the intervening years.
This space for rent
|
|
|
|
|
Not the one I was thinking of, I don't think - but it's a damn good 'un!
Someone had a link in their sig, IIRC - but for the life of me I can't recall who...
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
It was Dave Kreskowiak who had the links. First should have lead to this[^] article. The second one leads to this[^].
This space for rent
|
|
|
|
|
Dats de bunny!
He removed the link, which is why I got confused, probably ... that or the inevitable rise of senility.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
One of the links went dead so it was removed. Actually, I think it was either you or Pete that told me of it!
I'll gladly put them back... after I wake up.
System.ItDidntWorkException: Something didn't work as expected.
-- said no compiler, ever.
C# - How to debug code[ ^].
Seriously, go read this article.
Dave Kreskowiak
|
|
|
|
|
I think I mentioned the plural, but not the original dead link.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I put the links back in, with only 4 characters to spare. We need a bigger signature space.
|
|
|
|
|
I've got 360 spare at the mo, you can have 150 if you need them?
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Awsome! Thank you! I'll just ask Chris to increase my credit limit.
|
|
|
|
|
Sorry, it's 1:30am here and I've been working all day, so I may have missed something in that article that discusses alternatives to CodeProject. I'll look again though.
|
|
|
|
|
It's not discussing alternatives to CodeProject; what it's showing is how to write a question that will get answered here on CodeProject. To be fair, the same guidelines apply to pretty much any programming forum - in general the people answering will all have the same expectations.
This space for rent
|
|
|
|
|
Oh yeah, for sure. I posted at least one question that I thought was going to lead to a simple yes or no response, but since I didn't explain why I was asking such a basic question, it looked like I hadn't put forth any effort at all.
|
|
|
|
|
You've been a member since 2008 and have been an MVP for all but two of those years. So I'm guessing you didn't start learning C# at a time when there's an ENORMOUS volume of outdated information that looks just as current Online today as it did 15 years ago when it was published. A lot of it doesn't have dates either, because the web site owner whats it to appear fresh to the user.
It's a different ballgame today.
I was hoping for a different resource where conversation more closely resembles a discussion with a colleague.
Also, everyone has a different definition of "thoroughly" and so I think my rule will be to ask whatever questions I have, brush off any negativity I experience if someone else thinks my question is stupid, and just remember the struggle when I get really good, so I can encourage others to continue learning the language. Without that, it's just ColdFusion.
I was about as good as one could get in classic ASP back in 2000... a lot of good that's doing me now.
|
|
|
|
|
Surprisingly perhaps, C# was released in 2002, and I started learning it in ... 2008, when I joined this site. Before that, I was mostly embedded C / assembler with some Windows C++ and VB experience.
So much of the resources I had then were (relatively) just as outdated as the ones you have now!
There is a lot more bollocks and misinformation now, because anyone who can use google and cobble together an app from SO fragments (Stack Overflow Patchwork | CommitStrip[^]) thinks they know it all and can produce a YouTube video on C# ... but there was a good amount of that back then as well.
Rick_Bishop wrote: I was hoping for a different resource where conversation more closely resembles a discussion with a colleague.
That's called "mentoring", or "tutoring" and it's a considerable amount of work for the mentor / tutor - time that often just isn't available because everyone here is a volunteer, and most of 'em have paying jobs which - understandably - have priority. And how many people can you mentor / tutor on a one-to-one basis at a time? Very few, if you are going to do it properly, and fewer still if you are doing it for free! That said, any question where the asker is showing interest can easily turn into a discussion, and does regardless of where it is posted.
Rick_Bishop wrote: I think my rule will be to ask whatever questions I have, brush off any negativity I experience
Negative feedback in the most important and valuable feedback you can get! But only if you act on it and understand why it's there: is it something you did (asked a stupid question or asked it very badly) or is it the respondent who the problem? Or is it that the question you asked relies on context that you haven't supplied because you know all about that (but the reader doesn't have any access to)? As the article(s) Pete linked to say "asking questions is a skill" and if you don't develop the skill, you will get negative comments. Just brushing it off doesn't help, it hides any problems from the only person who can fix them: you. Don't ignore negative comments - they can help you seriously improve the way you think.
Basically remember this: a lazy question gets a lazy answer. If you think about what you are asking, and how you ask it, that shows. It produces a good question which encourages a good answer. And very often it gives you the solution yourself, which is why I sometimes tell people to "explain that as if you were explaining it to your mother on the phone". The effort you have to do to explain what you code does (or should do, or doesn't do) to a non technical mother without the benefit of diagrams is substantial - but it forces you to think about your presumptions about what you have been trying to do.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Thank you. That was well said. I hope others read this too.
The lack of surety in programming is part of the reason software is fragile.
|
|
|
|
|