2023-01-05, 16:59
  #13
Medlem
Citat:
Ursprungligen postat av bithax
"Dot syntax" är ju ändå en del av språket. Men det ska man då inte använda.
Du missar poängen. Jag använder funktionell stil så ofta som möjligt. Det ger mig mer lättläst, och mindre kod med färre buggar. T.ex. använder jag mycket Linq, det är en form av funktionell programmering.

En challenge: skriv om följande kod till klassisk, imperativ stil så jämför vi sedan?

Kod:
List<Tuple<stringstring>> tuples //
var grouped tuples.GroupBy(=> t.First)
    .
OrderBy(grp => grp.Key)
    .
Select(grp => new { Key grp.KeyItems grp.OrderBy(=> t.Second) }); 

Den stora charmen med detta är att jag inte skapar en massa egna tillstånd i programmet med olika variabler. I stället säger jag vad jag vill ha och då får jag det. Det finns färre möjligheter att göra fel och jag ser mycket snabbare vad koden gör.
Citera
2023-01-07, 11:00
  #14
Medlem
bithaxs avatar
Citat:
Ursprungligen postat av Binary
Du missar poängen. Jag använder funktionell stil så ofta som möjligt. Det ger mig mer lättläst, och mindre kod med färre buggar. T.ex. använder jag mycket Linq, det är en form av funktionell programmering.

En challenge: skriv om följande kod till klassisk, imperativ stil så jämför vi sedan?

Kod:
List<Tuple<stringstring>> tuples //
var grouped tuples.GroupBy(=> t.First)
    .
OrderBy(grp => grp.Key)
    .
Select(grp => new { Key grp.KeyItems grp.OrderBy(=> t.Second) }); 

Den stora charmen med detta är att jag inte skapar en massa egna tillstånd i programmet med olika variabler. I stället säger jag vad jag vill ha och då får jag det. Det finns färre möjligheter att göra fel och jag ser mycket snabbare vad koden gör.

Nu kanske just detta är ett exempel där linq passar väldigt bra, men i många fall så inser man att man kan optimera och ta bort minst en loop om man skriver koden utan linq.

Men vi kan testa. Jag skrev om ditt program så att det fungerar:

Kod:
List<Tuple<string, string>> tuples = new List<Tuple<string, string>>();
tuples.Add(Tuple.Create("1", "pelefant"));
tuples.Add(Tuple.Create("1", "apa"));
tuples.Add(Tuple.Create("2", "jagular"));
tuples.Add(Tuple.Create("2", "platypus"));
tuples.Add(Tuple.Create("3", "unicorn"));

var result = tuples.GroupBy(x => x.Item1)
    .OrderBy(grp => grp.Key)
    .Select(grp => new { Key = grp.Key, Items = grp.OrderBy(t => t.Item2) });

foreach(var r in result)
{
    foreach (var i in r.Items)
    {
        Console.WriteLine($"{i}");
    }
}

Och här är samma kod i imperativt format villket blev 26 rader i stället för 19 rader.
Kod:
List<Tuple<string, string>> tuples = new List<Tuple<string, string>>();
tuples.Add(Tuple.Create("1", "pelefant"));
tuples.Add(Tuple.Create("1", "apa"));
tuples.Add(Tuple.Create("2", "jagular"));
tuples.Add(Tuple.Create("2", "platypus"));
tuples.Add(Tuple.Create("3", "unicorn"));

Dictionary<string, List<Tuple<string, string>>> groupedTuples = new Dictionary<string, List<Tuple<string, string>>>();

foreach (var tuple in tuples)
{
    if (!groupedTuples.ContainsKey(tuple.Item1))
    {
        groupedTuples[tuple.Item1] = new List<Tuple<string, string>>();
    }
    groupedTuples[tuple.Item1].Add(tuple);
}

foreach (var key in groupedTuples.Keys.OrderBy(k => k))
{
    var sortedTuples = groupedTuples[key].OrderBy(t => t.Item2);
    foreach (var tuple in sortedTuples)
    {
        Console.WriteLine($"{tuple}");
    }
}

Nu tycker jag egentligen inte att linq är det värsta som C# har att erbjuda.
De kunde ha kallat select för map i stället och haft reduce, dvs samma som javascript.
Men så bra skulle det ju inte vara.

Det jag stör mig på mest är folk som sitter och hittar på egena funktionella syntaxer
t.ex. genom att skapa en massa olika statiska metoder. Jo det är ju fint, men jag kan ju inte ditt
funtionella språk du har hittat på. Linq är ju i alla fall en del av språket och något man kan förvänta
sig att folk som sätter sig in i ett nytt projekt kan grunderna i.
__________________
Senast redigerad av bithax 2023-01-07 kl. 11:32.
Citera
2023-01-07, 11:48
  #15
Medlem
Citat:
Ursprungligen postat av bithax

Det jag stör mig på mest är folk som sitter och hittar på egena funktionella syntaxer
t.ex. genom att skapa en massa olika statiska metoder. Jo det är ju fint, men jag kan ju inte ditt
funtionella språk du har hittat på. Linq är ju i alla fall en del av språket och något man kan förvänta
sig att folk som sätter sig in i ett nytt projekt kan grunderna i.

Det kallas för domain specific language, domänspecifiktspråk. Och är ett mycket bra sätt att öka läsbarheten i ett komplext system. Här är ett exempel från ett av mina system där jag är systemarkitekt i.

Kod:
        private MatchOutcomeWithReason GetJobOutcome(MatchJob job)
        {
            if (job.LooseMatch) return MatchOutcome.Refund.Because(RefundReason.OcrMissing);
            if (job.Payments.Any() && job.Invoice.InvoiceState == InvoiceState.Paid) return MatchOutcome.Refund.Because(RefundReason.AlreadyPaid);
            if (!job.CanPay) return MatchOutcome.Refund.Because(RefundReason.WrongOrder);
            if (job.Invoice.InvoiceState == InvoiceState.Paid) return MatchOutcome.Noop;
            if (job.Diff != 0) return MatchOutcome.Deviation;

            return MatchOutcome.Paid;
        }
__________________
Senast redigerad av CyberVillain 2023-01-07 kl. 11:50.
Citera
2023-01-07, 12:53
  #16
Medlem
bithaxs avatar
Citat:
Ursprungligen postat av CyberVillain
Det kallas för domain specific language, domänspecifiktspråk. Och är ett mycket bra sätt att öka läsbarheten i ett komplext system. Här är ett exempel från ett av mina system där jag är systemarkitekt i.

Kod:
        private MatchOutcomeWithReason GetJobOutcome(MatchJob job)
        {
            if (job.LooseMatch) return MatchOutcome.Refund.Because(RefundReason.OcrMissing);
            if (job.Payments.Any() && job.Invoice.InvoiceState == InvoiceState.Paid) return MatchOutcome.Refund.Because(RefundReason.AlreadyPaid);
            if (!job.CanPay) return MatchOutcome.Refund.Because(RefundReason.WrongOrder);
            if (job.Invoice.InvoiceState == InvoiceState.Paid) return MatchOutcome.Noop;
            if (job.Diff != 0) return MatchOutcome.Deviation;

            return MatchOutcome.Paid;
        }

Jo men bygg det där och så gå och sluta och skit i att dokumentera nått. Nästa person som kommer in efter dig börjar om från början och skriver ett nytt system från grunden med en ny DSL som han tycker är mycket bättre. Så håller det på sådär.

Vi har också haft en arkitekt som suttit och byggt dsl i sin ”kontomodell” och ja, det är ju synd om dem som ska underhålla det där. Jag kommer inte göra det. Sa upp mig när han sa upp sig.

Problemet är att man inte vet vad som pågår under. Och steppar du in i det med en debugger, ja då är det mesta one-liners som kallar på andra one-liners. Tänkt då att man ska ändra nått där inne. Eller om man behöver lägga till funktionalitet.

Hitintills har jag inte sett någon hemmagjord DSL som följer SOLID principerna. Det går säkert att bygga sådana men vore enklare med en vanlig domänmodell med objekt.
__________________
Senast redigerad av bithax 2023-01-07 kl. 13:19.
Citera
2023-01-07, 14:05
  #17
Medlem
Citat:
Ursprungligen postat av bithax
Jo men bygg det där och så gå och sluta och skit i att dokumentera nått. Nästa person som kommer in efter dig börjar om från början och skriver ett nytt system från grunden med en ny DSL som han tycker är mycket bättre. Så håller det på sådär.

Vi har också haft en arkitekt som suttit och byggt dsl i sin ”kontomodell” och ja, det är ju synd om dem som ska underhålla det där. Jag kommer inte göra det. Sa upp mig när han sa upp sig.

Problemet är att man inte vet vad som pågår under. Och steppar du in i det med en debugger, ja då är det mesta one-liners som kallar på andra one-liners. Tänkt då att man ska ändra nått där inne. Eller om man behöver lägga till funktionalitet.

Hitintills har jag inte sett någon hemmagjord DSL som följer SOLID principerna. Det går säkert att bygga sådana men vore enklare med en vanlig domänmodell med objekt.

Det är klart att just detta exempel följer inte open/closed principen i SOLID då man måste ändra på metoden för att införa fler matching outcomes. Själv tycker jag man ibland kan bryta mot open closed som i detta fallet. Man måste vara lite pragmatisk.

Å andra sidan tycker jag man ska implementera strategier så ofta man kan just för att följa open / closed principen.

Har egentligen inget med DSL att göra. DSL gör koden mer lättläst och mer som att läsa en mening i klartext.
Citera
2023-01-07, 14:34
  #18
Medlem
bithaxs avatar
Citat:
Ursprungligen postat av CyberVillain
Det är klart att just detta exempel följer inte open/closed principen i SOLID då man måste ändra på metoden för att införa fler matching outcomes. Själv tycker jag man ibland kan bryta mot open closed som i detta fallet. Man måste vara lite pragmatisk.

Å andra sidan tycker jag man ska implementera strategier så ofta man kan just för att följa open / closed principen.

Har egentligen inget med DSL att göra. DSL gör koden mer lättläst och mer som att läsa en mening i klartext.

Ja ta t.ex masstransit konfigurationen där han tvingar en att konfigurera en AWS region innan du kan göra nått annat. Det hade varit så mycket bättre att bara kunna skicka in en AWS konfiguration direkt, men nej, han ska ju skapa den åt dig. Ännu bättre hade varit att kunna skicka in en factory som skapar AWS klienten.

AWS klienten kan konfigureras med environmentvariabler, men det går inte att använda när DSLen skapar en config åt dig och ramverket skapar en AWS klient åt dig.

Detta är bara ett exempel, men jag har sett sånthär minst 10 gånger att man hindrar injection för man har nån DSL som ska ”göra det bättre.

Använd dependency injection istället, eller ge åtminstone ett alternativ att använda injection. Om man inte gör det så kommer inte dehär apierna överleva tidens tand. I alla fall inte utan någon som hela tiden sitter och uppdaterar dem.
__________________
Senast redigerad av bithax 2023-01-07 kl. 14:40.
Citera
2023-01-07, 15:26
  #19
Medlem
Citat:
Ursprungligen postat av bithax
Ja ta t.ex masstransit konfigurationen där han tvingar en att konfigurera en AWS region innan du kan göra nått annat. Det hade varit så mycket bättre att bara kunna skicka in en AWS konfiguration direkt, men nej, han ska ju skapa den åt dig. Ännu bättre hade varit att kunna skicka in en factory som skapar AWS klienten.

AWS klienten kan konfigureras med environmentvariabler, men det går inte att använda när DSLen skapar en config åt dig och ramverket skapar en AWS klient åt dig.

Detta är bara ett exempel, men jag har sett sånthär minst 10 gånger att man hindrar injection för man har nån DSL som ska ”göra det bättre.

Använd dependency injection istället, eller ge åtminstone ett alternativ att använda injection. Om man inte gör det så kommer inte dehär apierna överleva tidens tand. I alla fall inte utan någon som hela tiden sitter och uppdaterar dem.

Just masstransits ide är ju att man ska köra med de conventions som är uppsatta av masstransit när det gäller köer. Kör det på jobbet för att management inte ville betala för nServicebus som jag vill använda. Har inget att invända mot just den biten. Men masstransit är rätt dåligt implementerat över lag. Saker silent fallerar utan errors osv.

Edit: masstransit använder IOC under utan dock och inget hindrar dig dig byta ut dessa
__________________
Senast redigerad av CyberVillain 2023-01-07 kl. 15:28.
Citera
2023-01-07, 16:24
  #20
Medlem
Citat:
Ursprungligen postat av bithax
Nu kanske just detta är ett exempel där linq passar väldigt bra, men i många fall så inser man att man kan optimera och ta bort minst en loop om man skriver koden utan linq.

Men vi kan testa. Jag skrev om ditt program så att det fungerar:

Kod:
List<Tuple<string, string>> tuples = new List<Tuple<string, string>>();
tuples.Add(Tuple.Create("1", "pelefant"));
tuples.Add(Tuple.Create("1", "apa"));
tuples.Add(Tuple.Create("2", "jagular"));
tuples.Add(Tuple.Create("2", "platypus"));
tuples.Add(Tuple.Create("3", "unicorn"));

var result = tuples.GroupBy(x => x.Item1)
    .OrderBy(grp => grp.Key)
    .Select(grp => new { Key = grp.Key, Items = grp.OrderBy(t => t.Item2) });

foreach(var r in result)
{
    foreach (var i in r.Items)
    {
        Console.WriteLine($"{i}");
    }
}

Och här är samma kod i imperativt format villket blev 26 rader i stället för 19 rader.
Kod:
List<Tuple<string, string>> tuples = new List<Tuple<string, string>>();
tuples.Add(Tuple.Create("1", "pelefant"));
tuples.Add(Tuple.Create("1", "apa"));
tuples.Add(Tuple.Create("2", "jagular"));
tuples.Add(Tuple.Create("2", "platypus"));
tuples.Add(Tuple.Create("3", "unicorn"));

Dictionary<string, List<Tuple<string, string>>> groupedTuples = new Dictionary<string, List<Tuple<string, string>>>();

foreach (var tuple in tuples)
{
    if (!groupedTuples.ContainsKey(tuple.Item1))
    {
        groupedTuples[tuple.Item1] = new List<Tuple<string, string>>();
    }
    groupedTuples[tuple.Item1].Add(tuple);
}

foreach (var key in groupedTuples.Keys.OrderBy(k => k))
{
    var sortedTuples = groupedTuples[key].OrderBy(t => t.Item2);
    foreach (var tuple in sortedTuples)
    {
        Console.WriteLine($"{tuple}");
    }
}

Nu tycker jag egentligen inte att linq är det värsta som C# har att erbjuda.
De kunde ha kallat select för map i stället och haft reduce, dvs samma som javascript.
Men så bra skulle det ju inte vara.

Det jag stör mig på mest är folk som sitter och hittar på egena funktionella syntaxer
t.ex. genom att skapa en massa olika statiska metoder. Jo det är ju fint, men jag kan ju inte ditt
funtionella språk du har hittat på. Linq är ju i alla fall en del av språket och något man kan förvänta
sig att folk som sätter sig in i ett nytt projekt kan grunderna i.
Fast nu fuskade du lite och använda Linq-metoden OrderBy. Dvs, du använde faktiskt funktionell programmering i ditt exempel där du försökte visa att imperativ stil är bättre. Map, Reduce är också funktionell programmering. Så egentligen gillar du det, du är bara inte överens med hur det är designat eller namngivet i .net?

Om du även hade skrivit en egen implementation för sortering, eller sortering på flera fält då hade du tydligare sett fördelen med funktionell stil. Ju mer komplext det blir, desto bättre blir funktionell programmering. Folk som jobbar mycket med komplicerade beräkningar använder ofta funktionella språk.

Att skapa egna lager med funktionella anrop är ofta en bra idé tycker jag.
Kodens syfte blir mycket enklare att förstå om du får in mer affärsregler och begrepp. T.ex
list.map(...) //innehåller logik som räknar om priser till EUR
Vs:
shoppingcart.mapToCurrency(..., currencyInfo) //mycket bättre kod IMHO

Däremot godkänner jag inte att göra sådant som globala extensions, då får man God objects som vet allt, det är inte bra. Typ string.ParseCustomer()
Citera
2023-01-07, 16:35
  #21
Medlem
Citat:
Ursprungligen postat av CyberVillain
Det kallas för domain specific language, domänspecifiktspråk. Och är ett mycket bra sätt att öka läsbarheten i ett komplext system. Här är ett exempel från ett av mina system där jag är systemarkitekt i.

Kod:
        private MatchOutcomeWithReason GetJobOutcome(MatchJob job)
        {
            if (job.LooseMatch) return MatchOutcome.Refund.Because(RefundReason.OcrMissing);
            if (job.Payments.Any() && job.Invoice.InvoiceState == InvoiceState.Paid) return MatchOutcome.Refund.Because(RefundReason.AlreadyPaid);
            if (!job.CanPay) return MatchOutcome.Refund.Because(RefundReason.WrongOrder);
            if (job.Invoice.InvoiceState == InvoiceState.Paid) return MatchOutcome.Noop;
            if (job.Diff != 0) return MatchOutcome.Deviation;

            return MatchOutcome.Paid;
        }
Nä, detta är ett försök att göra DDD med method chaining, det är inte en DSL.
SQL är en DSL, det är ett begränsat, inte generellt språk för att hantera data i databaser.
Citera
2023-01-07, 21:38
  #22
Medlem
Citat:
Ursprungligen postat av Binary
Nä, detta är ett försök att göra DDD med method chaining, det är inte en DSL.
SQL är en DSL, det är ett begränsat, inte generellt språk för att hantera data i databaser.

Det är en form av DSL absolut. Väldigt lokalt och begränsat självfallet. Men DSL. Fluent APIer som dessa är bra exempel på funktionell programmering när den används korrekt.
Citera
2023-01-07, 22:12
  #23
Medlem
Citat:
Ursprungligen postat av CyberVillain
Det är en form av DSL absolut. Väldigt lokalt och begränsat självfallet. Men DSL. Fluent APIer som dessa är bra exempel på funktionell programmering när den används korrekt.
Fortfarande nej. DSL = Domain Specific Language.
Det är om du uppfinner ett eget språk för din domän. Men om du i C# skriver t.ex.
MatchOutcome.Refund.Because(RefundReason.WrongOrde r)
Då är detta språket C# fortfarande, och inte ett eget konstruerat språk.
Du har här properties och metoder som är trevligt namgivna, troligt inspirerat av DDD, Domain Driven Design, men det är inte därför ett eget språk, en egen DSL.
Citera
2023-01-08, 00:25
  #24
Medlem
Citat:
Ursprungligen postat av Binary
Fortfarande nej. DSL = Domain Specific Language.
Det är om du uppfinner ett eget språk för din domän. Men om du i C# skriver t.ex.
MatchOutcome.Refund.Because(RefundReason.WrongOrde r)
Då är detta språket C# fortfarande, och inte ett eget konstruerat språk.
Du har här properties och metoder som är trevligt namgivna, troligt inspirerat av DDD, Domain Driven Design, men det är inte därför ett eget språk, en egen DSL.

Nej du har fel. Du kan bygga DSL i ett annat språk. Det kallas för intern DSL.


Citat:
External and Embedded Domain Specific Languages

DSLs implemented via an independent interpreter or compiler are known as External Domain Specific Languages. Well known examples include LaTeX or AWK. A separate category known as Embedded (or Internal) Domain Specific Languages are typically implemented within a host language as a library and tend to be limited to the syntax of the host language, though this depends on host language capabilities.


__________________
Senast redigerad av CyberVillain 2023-01-08 kl. 00:28.
Citera

Skapa ett konto eller logga in för att kommentera

Du måste vara medlem för att kunna kommentera

Skapa ett konto

Det är enkelt att registrera ett nytt konto

Bli medlem

Logga in

Har du redan ett konto? Logga in här

Logga in