[HN Gopher] The combined power of F# and C#
___________________________________________________________________
The combined power of F# and C#
Author : freedude
Score : 133 points
Date : 2023-08-07 18:19 UTC (4 hours ago)
(HTM) web link (steven-giesel.com)
(TXT) w3m dump (steven-giesel.com)
| guhidalg wrote:
| I developed a very non-trivial ASP.NET Core application mixing F#
| and C#. I learned you have to take the best parts of both
| languages to get maximum benefits. Specifically you're going to
| want a project graph that looks like this:
|
| 1. Your top-level project should be a csproj where all your "web"
| stuff is. Controller routes, DTOs, startup code, etc... This
| project is responsible for converting data into nice objects that
| are passed to the F# code.
|
| 2. A mid-tier project written in F#, an fsproj. This is where you
| can be nice, pure, functional, live out your best life pretending
| C# doesn't exist. However you will eventually run into issues
| interfacing with external libraries, EF Core, lack of standard
| library support for whatever you're doing. That's ok! The final
| layer will solve it.
|
| 3. A foundational project written in C#. This project's job is to
| hide the ugly and make your F# project into the functional
| programming nirvana you know it can be. Wrap those dependencies
| into pure functions, write your entity framework junk, use all
| the latest C# language and runtime features and expose them to
| your F# project.
|
| So something like:
|
| C# -> F# -> C#
|
| Ultimately, I decided that C# really is good enough and I didn't
| see enough benefits to continue using F#.
| ComputerGuru wrote:
| > Ultimately, I decided that C# really is good enough and I
| didn't see enough benefits to continue using F#.
|
| I've heard this story repeated so many times over the years.
| The responsiveness of the C# language team in particular (as
| opposed to the runtime or ASP teams, etc) is remarkable and the
| thoughtful inclusion of functional-inspired features over the
| years has really dramatically improved QOL for C# devs. (With
| the exception of some annoying carve outs like going too far
| with trying to please JS/node refugees with not only
| introducing support for top-level statements but also pushing
| it as the default in the various templates, the botched
| delivery of global includes, and other such changes --- but to
| be fair, many of these were implemented correctly and without
| any complaints on my end in the language itself but my quibbles
| are more with the delivery, integration, and polish in the IDE
| and downstream by teams like the ASP.NET Core one.)
|
| After a long pause during the denouement days of the .NET
| Framework, the C# of today would be unrecognizable to C# devs
| of yesteryear, and the changes keep coming.
|
| (All this is from the POV of a developer that's been using C#
| since 2002; back when J# was a thing and I was slowly adapting
| to writing code in a case-sensitive manner after years of VB.)
| [deleted]
| daxfohl wrote:
| Funny, I did exactly the opposite! I really enjoyed f#'s
| functional-style web framework Suave, but put it on top of
| services written in OO style, which all used immutable f#
| records and lists under the hood. It ended up being a lot
| easier to do all this in f# than mix the two languages, since
| f# is dual purpose. I called it sandwich-oriented programming.
| wizofaus wrote:
| Surely C# qualifies more as "multi-purpose" than F# does?
| It's certainly more of a multi-paradigm language - my own C#
| code freely mixes functional, imperative and OO- style
| (multiple implementations of the same interface etc.)
| techniques, the latter two of which I struggle to envision
| being improved by rewriting in F#.
| guhidalg wrote:
| Using a framework like Suave probably reduces the "impedance
| mismatch" between ASP.NET and F#. If I had used that, I
| probably wouldn't have needed to split the code base into C#
| and F# parts.
| evntdrvn wrote:
| I've tried to use "ASP.NET Core with F# underneath" for
| several projects, thinking it would make for an easier
| bridge for the rest of the team members who were very
| comfortable with asp.net already.
|
| And each time, it turned out to be a mistake.
|
| Things went way more smoothly by just using an F#-native
| web framework like Falco or Suave or Giraffe or ...
| pjmlp wrote:
| Yes, that is the ultimate problem with F#, Microsoft behaves as
| if it was a kind of mistake to add it to the Visual Studio
| 2010, the experience always lags behind C# and VB, leaves to
| the community to do most of the work, and now with stuff like
| code generators being used everywhere is getting worse.
| soulbadguy wrote:
| +1
| phillipcarter wrote:
| This is an excellent distillation of how you can effectively
| mix F# and C# in the same codebase.
| Rapzid wrote:
| F# deserves a project like Phoenix that the entire community
| can get behind. World-class docs, zero getting started
| friction(docs, examples, and libraries all just work), and
| bomber F# APIs over any ubiquitous C# libs it might need.
|
| It's just too piecemeal right now.
| bob1029 wrote:
| I know it's a really trivial matter, but I actually got to use
| the tuple swap thing the other day and it felt _so_ good.
| (ItemA, ItemB) = (ItemB, ItemA)
|
| ~50% of our methods don't even have block bodies anymore...
| Switch expressions are our bread & butter now. Once you learn how
| to combine expressions + switch + LINQ, you have the triforce of
| poor-man's functional programming inside the perfect imperative
| realm by which to protect it.
| raydiatian wrote:
| Say it with me everybody
|
| "Trade offs"
| michaelteter wrote:
| Functional core, imperative shell. This seems to be a good
| approach, especially when you push pretty hard to stuff as much
| as possible into the functional core.
|
| Writing tests is much easier and requires much less code, and
| bugs are much easier to track down (since you have less surface
| area of "what might have modified this object I now hold?").
|
| Since a functional approach also goes well with single-
| responsibility-principle and composability, you can end up with
| multiple times more actual functions. They're all (ideally) very
| conceptually simple, but the larger number of more specialized
| functions means you have to work a lot harder on choosing
| meaningful names. And to write meaningful names of specific
| functions, you can end up with some quite long names. This is
| actually quite ok as it makes things much more readable, but some
| people have some strong negative reactions to seeing 4-6 word
| function names. But at least in this case (Java being the most
| famous counter scenario) each function probably has more real
| value as it's not just OO layer artefacts.
| seanwilson wrote:
| Can anyone explain why not "functional core, functional shell"?
| Is this partly because the shell has to mostly interact with
| things like UI APIs, file system APIs, network APIs and
| database APIs that are usually implemented in an imperative way
| already so you'd make your life harder this way? What if the
| APIs were written in a functional style?
|
| For example, I find in TypeScript, most libraries and the
| language itself are not written with immutability in mind so
| there's constant gotchas. So while I strongly prefer immutable
| data structures, going against the grain to use immutability
| everywhere usually isn't worth the frustration. That for me
| doesn't mean immutability is a bad idea though, it's more that
| the libraries and languages support is lacking.
| xupybd wrote:
| The shell is where you put all your side effects. Things like
| user input or writing to files.
|
| That way you can use pure functions on the inside. Pure
| functions don't allow for side effects.
| seanwilson wrote:
| Why does "imperative" imply "side effects" here though?
| Pure functional languages have ways of modelling side
| effects.
| xupybd wrote:
| They do but it's a bit of a square peg round hole. It's
| just easier not to use things like the IO monad.
| WorldMaker wrote:
| It's also fun to note that the IO monad in some
| perspectives is an abstraction _for_ "functional core,
| imperative shell" moving as much of the imperative "order
| matters" computations to an outer "shell". The IO monad
| in some ways _is_ the way of mathematically representing
| the "shell" of an application.
| turboponyy wrote:
| Pure functions can't perform side effects, but they can
| describe them.
|
| For a functional shell, your program's entry point would be
| composed of pure functions describing effectful actions to
| take. The necessary computations get bubbled up to the
| entry point during evaluation, at which point the runtime /
| compiled program executes them.
|
| (At least, this is the abstracted perspective you should
| view the purely functional source code from. The final
| program itself in practice is, of course, effectful
| throughout).
| lmm wrote:
| > Can anyone explain why not "functional core, functional
| shell"? Is this partly because the shell has to mostly
| interact with things like UI APIs, file system APIs, network
| APIs and database APIs that are usually implemented in an
| imperative way already so you'd make your life harder this
| way? What if the APIs were written in a functional style?
|
| Fundamentally the user isn't functional - if you ask them
| what they want to do they'll say different things at
| different times. You can have a 100% purely functional
| programming language that works like a calculator (with no
| memory) - the user can put in expressions and the language
| will evaluate them - but generally users want their language
| to be able to _do_ things that are non-idempotent and non-
| time-symmetric and so on. You can, and should, push that part
| to the edge, but it needs to be there somewhere.
| kmac_ wrote:
| This approach works very well. Side effects are pushed out of
| core and no mocks are required for end to end tests. It's quite
| hard to "sell" to fellow devs, but when it clicks, the code is
| very clean.
| gugagore wrote:
| One thing that I have seen in the Julia ecosystem, where
| multiple dispatch is pervasive, is that function names can get
| away with only being verbs. Without dispatch, you often see an
| approximation by baking in (Hungarian notation) the name of a
| type into the function.
|
| This is most clearly seen with conversation functions between
| types defined in different libraries. A collection class might
| have a method like "from_Array".
| bob1029 wrote:
| > Functional core, imperative shell.
|
| You can find this pattern in a lot of places if you are a
| little bit flexible with your definition of "functional".
|
| For example, why would we not consider some combination like
| SQL and PHP to be hinting at this exact same kind of archetype?
| You've got a crusty outer shell of yucky hackarounds (PHP) that
| talks to this (ideally) well-normalized & clean data store
| (SQL). Assuming you "stuff as much as possible" into core part
| of the solution, you could swap the outer shell for anything
| you desire without much headache.
| skrebbel wrote:
| PHP is more like "imperative core, functional shell". After
| all, a PHP request is a pure stateless function call. The
| only way it can modify state anywhere is with IO (eg store
| something in the database, write something to the session
| store, etc) and all data you accumulate in your PHP code gets
| purged after each request.
| closeparen wrote:
| And SQL is all about mutation... functional take on the
| database would be something like Datomic.
| leblancfg wrote:
| Someone at Microsoft better get a trademark on "Powerchord".
| Const-me wrote:
| The C# version is slightly better, though.
|
| Binary floating-point numbers are less than ideal for keeping
| discount percentage because a value like 12.3% can't be
| represented exactly.
| wizofaus wrote:
| Not sure why it's not "Percentage of decimal" though?
| Const-me wrote:
| Absolutely, it better be a decimal. These numbers in .NET are
| decimal floating-points, they can represent all these 12.34%
| without any loss of precision.
| munchler wrote:
| I love seeing F# mentioned on Hacker News, but this article in
| particular is a bit dubious. Yes, you can use F# from C#, but it
| gets frustrating quickly.
|
| Fortunately, almost anything you can do in C# can be done better
| in F# anyway, so there's really very little need to use C# at all
| once you take the plunge into functional programming.
| gsuuon wrote:
| I agree - I guess this article is more about a (very) gradual
| way to adopt F# into an existing C# code base, rather than an
| inherently useful combination of C# and F#.
| munchler wrote:
| True, but for any serious integration the F# library should
| expose an API that's designed to be more idiomatic for C#
| callers. When I see `ListModule.OfSeq` being called from C#,
| I cringe a little.
| ctenb wrote:
| You could make the C# code shorter by using the one-liner record
| syntax. That would compare the SLOC more fairly.
| einpoklum wrote:
| > Where C# is the most dominant language in the .NET world
|
| Why would we want to live in the ".NET world"? I say adopt a
| programming language, or languages-ecosystem, that's not useful
| only in the "[some niche] world", but more-or-less everywhere.
| Whether it's older and well-trodden or newer and up-and-coming, I
| don't see the benefit of limiting yourself that way.
| WorldMaker wrote:
| It is not really a "niche". The ".NET world" _is_ more-or-less
| everywhere: it is cross-platform, open source, and runs on
| Linux /Windows/macOS/iOS/Android/more.
|
| The ".NET world" is comparable to the "JVM world" or even the
| "LLVM world": a virtual machine target with a varied language
| ecosystem and a lot of real machines supported at runtime (in a
| cross-platform, open source way today).
|
| ".NET world" is defining a complex language ecosystem, and it
| probably isn't as "limited" an ecosystem as you seem to think.
| jimmaswell wrote:
| The F# is certainly nice, but the C# is odd. This is a textbook
| example of either inheritance or interfaces. I did inheritance
| here but it would be trivial to change it to an interface.
|
| https://dotnetfiddle.net/F5fPZy
|
| The class implementations add a bit of upfront verbosity but the
| calculation at the end is arguably nicer to read: that method is
| concerned with applying the discounts, but in the F# case it has
| to know how to apply each type of discount, while the C#
| encapsulates that in the Discount implementation.
| decimal subtotal = OrderLines.Sum(ol => ol.Product.Price \*
| ol.Quantity); return Discount?.Apply(subtotal) ?? subtotal;
|
| vs let subtotal = List.sumBy (fun ol ->
| ol.Product.Price \* (decimal ol.Quantity)) orderLines let
| totalDiscount = discount |> Option.map (fun d ->
| match d with | Percentage p -> subtotal \* (decimal p /
| 100M) | FixedAmount f -> f) |> Option.defaultValue
| 0M subtotal - totalDiscount
|
| Though I'm also pretty sure F# could do the same thing with the
| classes if you wanted. And someone else pointed out you can write
| some C# that looks the same as the F# if you use a switch
| statement. And I'm pretty sure you could make it look even more
| F# if you used more Linq.
|
| I guess the F# matching is pretty much like interface/class
| checking as follows? I don't see the functional difference:
| if (discount is IPercetage percentage) { .. subtotal \*
| percentage.value / 100 ... else if (discount is IFixed
| fixed) { .. subtotal - fixed.value ...
|
| And you could do even more with some newer C# features like
| anonymous classes etc.
|
| I'm not left feeling like I'm missing out on anything without
| discriminated unions in particular. I'd be interested to see some
| cases where the functional nature of F# does make a large
| improvement over the equivalent C# though.
| jackmott wrote:
| [dead]
| gsuuon wrote:
| You can also hide the logic away in F#: let
| calcDiscount = function | Percentage p ->
| subtotal * (decimal p / 100M) | FixedAmount f -> f
|
| And then: let totalDiscount = discount
| |> Option.map calcDiscount |> Option.defaultValue 0M
|
| F# is missing some of the convenience operators, but you could
| easily add these yourself if you wanted: let
| (?) x f = Option.map f x let (|?) x y =
| Option.defaultValue y x
|
| and so totalDiscount becomes: let totalDiscount
| = discount ? calcDiscount |? subtotal
|
| The benefit of DU's over inheritance for me is that you can't
| tie yourself to any additional details that get stuck onto
| Discount (since you can't extend DU's). If you have loosely
| coupled data you won't have sort-of-relevant methods possibly
| mutating shared state. The discount calc only takes a simple
| thing hidden away by the Discount DU, and so there are fewer
| plates you're trying to keep spinning in your head (all of
| "what could be").
| Smaug123 wrote:
| The main difference is the totality checking. A discriminated
| union guarantees that you've handled the _entire_ modelled
| domain (and nothing else); repeated interface checking merely
| guarantees that you 've handled a _subset_ of it (and also you
| may have handled a bunch of other stuff).
|
| It's so _comforting_ to know up front that you 've done
| everything you need to do!
| danjc wrote:
| Brevity and clarity are often conflated. Sometimes they're
| actually inversely correlated.
| hd4 wrote:
| While there's a thread on F#, I'd like to complain about how I
| attempted to create/publish a F# dotnet app in entirely using the
| terminal (i.e. without the bloat of Visual Studio) and it doesn't
| seem to be supported, which was unfortunate.
| dotnet new webapp -lang F# -o MyFSharpWebApp -f net8.0
|
| was the command I used. This was in Windows 10. I'll probably
| attempt it again in WSL2 when I have time, though I'm not
| expecting better results.
| phillipcarter wrote:
| This is, unfortunately, a consequence of the annoying naming
| Microsoft does (I am allowed to complain about this because I
| worked on the .NET team -- nobody can name things we can't).
|
| You'll note that in the list of default templates:
|
| ASP.NET Core Empty web [C#],F# Web/Empty
|
| ASP.NET Core gR... grpc [C#] Web/gRPC
|
| ASP.NET Core We... webapi [C#],F# Web/WebAPI
|
| ASP.NET Core We... webapp,razor [C#] Web/MVC/Razor Pages
|
| ASP.NET Core We... mvc [C#],F# Web/MVC
|
| ASP.NET Core wi... angular [C#] Web/MVC/SPA
|
| ASP.NET Core wi... react [C#] Web/MVC/SPA
|
| "webapp" is actually a flavor of MVC with Razor Pages to define
| the UI, which is itself a flavor of UI that's different from
| the typical HTML/JS/react setup most people use.
|
| I think the cleanest way to do web development with .NET is to
| leave .NET-isms out of the UI (.NET UI is, ironically, its
| weakest link now) and keep it on the server.
| Smaug123 wrote:
| The templates are all listed at https://learn.microsoft.com/en-
| us/dotnet/core/tools/dotnet-n... . You picked a template that
| is indeed not supported in F#; the F# ecosystem is generally
| oriented more around the SAFE stack and Fable.
| gsuuon wrote:
| > dotnet new list
|
| Shows all the available templates - you could give mvc or web a
| try?
| Dwedit wrote:
| Yay, the diagram of F#, C#, and VB .net. Because C++/CLI does not
| exist.
| xenadu02 wrote:
| There's another power when using the same underlying runtime
| environment as well: no bridging overhead. If your specific
| problem set involves a lot of boundary crossing bridging overhead
| can easily dominate all else.
|
| Long ago I integrated IronJS (written in F#) into a project
| written in C#. IronJS at the time was a very primitive JS
| compiler though it was a real compiler using the runtime
| machinery to emit Expression objects that were JIT'd into native
| code.
|
| I was very excited about switching to V8 for the vastly improved
| performance. To my shock and horror once I got it working the
| performance was worse. It turned out most scripts called our API
| a lot (the equivalent of the DOM in a browser). Bridging between
| CLR types and V8 (C++) types completely erased V8's superior
| performance. No amount of lazy bridging and zero-copy
| abstractions were enough and I eventually abandoned that branch.
|
| It was a humbling experience that I learned a great deal from.
| pjmlp wrote:
| C++/CLI is missing from the picture, even if it is only available
| on Windows.
| TillE wrote:
| C++/CLI really just exists for creating .NET bindings to C++
| libraries, right?
|
| If you've got a C interface, LibraryImport in C# is
| sophisticated enough to get the job done on its own.
| pjmlp wrote:
| Kind of, originally the plan was to have .NET everywhere.
|
| .NET 1.x brought Managed C++, which was replaced by C++/CLI
| in .NET 2.0.
|
| However Office and WinDev were never big fans of it, so its
| main purpose has been as easier interop for C++ libraries
| indeed.
|
| Also COM and C, as for anyone that is comfortable in C++, is
| is easier than getting all marshaling annotations correct.
|
| However it isn't an option for code that has to be portable,
| as it relies on Visual C++ toolchain.
| shortrounddev2 wrote:
| I want to like F#, but the syntax is just bonkers to me. There
| are keywords which are used in only one context, such as `then`,
| and sometimes an operator means something in one context, but
| something else in another. e.g: equality/assignment
| let a: int = 1 // Assignment a = 3 // Testing equality,
| will give you an error a <- 4 // Assignment, valid
|
| There's an article here with more things:
|
| https://alexyakunin.medium.com/crying-out-loud-what-i-like-d...
|
| I want to like F# since I love C#, but I just want something
| closer to Scala, but for .Net
| munchler wrote:
| The reason this bothers you is because you're trying to apply
| your imperative experience to a functional language. Once you
| stop doing that, F# will make a lot more sense. For example:
| let a: int = 1
|
| This isn't assignment; it's a binding that permanently
| associates the identifier `a` with the value `1`.
| Eji1700 wrote:
| This is such a weird example to me, mostly because I've yet to
| come across a language that doesn't have awkward gotchas, and
| that one is one of the easiest ("ah yeah...= is only assignment
| in a let..weird"). The article cited ends with -
|
| > That's it for now. Hopefully, some of these issues will be
| addressed in future versions of F#, though as I said, most of
| this is simply annoying / inconsistent. It shouldn't stop
| anyone from using this awesome language
|
| and as other have pointed out, some of this is complaining
| about features or not working with the language. File order
| matters should probably exist in more languages (the amount of
| debugging/code reading time it saves is insane) and complaining
| about explicit typing because it's slowing down your contest
| code is just eye rollingly silly to me.
| FrustratedMonky wrote:
| It takes a bit of time to grok. But then it becomes pretty
| simple and streamlined.
|
| Recently I did some F# conversion to Python, and the Python
| code really did seem ugly and ungainly in comparison.
| wizofaus wrote:
| Out of curiosity, what was the motivation for the conversion?
| And did you use a tool (ChatGPT even?) to help?
| FrustratedMonky wrote:
| I used FABLE, that is like F# transpiler to other
| languages. It compiles F# to other languages. The Python
| seems hard to read, but could be my unfamiliarity with
| Python.
| Smaug123 wrote:
| Some of the complaints in the article are certainly valid (why
| would there be early-return in the context of computation
| expressions, but not in ordinary functions?), but some I claim
| are actually features, and some are false.
|
| * Linearly ordered files. Several times in F# I have tracked
| down an issue simply by bisecting the codebase, which is
| impossible in C# because there's no meaningful way to halve the
| code. I think I've only once ever had a problem with the linear
| ordering that wasn't solved simply by reordering some files.
|
| * Explicit type conversions. I've only ever found this awkward
| when interfacing with C# code that is designed around C#'s
| extreme laxity. Explicit is better than implicit!
|
| * "No struct tuples" is false - that's what the `struct`
| keyword is for. (It may have been true when the article was
| written.)
|
| * Dot notation for indexing is now no longer necessary.
|
| It's certainly true that OO idioms are often clunky and feel
| strangely like they were constructed by Frankenstein, but then
| I almost never find myself trying to use them anyway. You don't
| notice oddities in features which you never think of using!
| Similarly, the number of times I use `<-` is so low that I
| don't think of it as being incongruously odd - why shouldn't
| there be a baroque syntax to indicate the place in your code
| that's likely to have a 50% higher chance of bugs?
| [deleted]
| WorldMaker wrote:
| > "No struct tuples" is false - that's what the `struct`
| keyword is for. (It may have been true when the article was
| written.)
|
| Quite probably. The underlying CLR ValueTuple type is a
| relatively recent .NET addition (and partly only exists
| because C# asked for it). (It was added in Fx 4.7 / Core 1.0,
| whereas the reference type Tuple was added way back in Fx
| 4.0.)
| Smaug123 wrote:
| I have been informed that computation expressions do _not_
| have early return. For example, `async` does allow you to
| express what appears to be an early return, but only of the
| `unit` type, and in fact it doesn 't return early; it simply
| proceeds straight past the `return` keyword. Raised
| https://github.com/dotnet/fsharp/issues/15759 .
| sasmithjr wrote:
| a <- 4 // Assignment, valid
|
| This is a compiler error because you haven't made your variable
| mutable.
|
| Also, I actually like that initial assignment and equality
| testing both use =. I think of `let a = 1` to be less like
| assignment and more like `Assume that a = 1 is true`, so using
| the same operator (e.g. `a = 3`) makes sense for comparisons.
|
| And `<-` as a separate operator is good because mutability
| should be exceptional for many (most?) codebases.
| shortrounddev2 wrote:
| Assume that a = 1 is true
|
| I just don't read code as I would a mathematical proof. I
| think in terms of what memory locations are equal to what in
| the stack, or the heap, and what is the lifecycle of that
| data in that memory address.
|
| When I read "int a = 1" in C#, I implicitly translate that to
| "take a 4 byte piece of memory on the stack, and set it equal
| to 1". I don't think in the abstract sense of a formula.
|
| When I see a class like: class Foo {
| int x = 2; string xyz = "Hello"; }
| var foo = new Foo();
|
| I read this as "allocate a chunk of memory big enough in the
| heap to insert a 4 byte integer and an 8 byte pointer. Set
| that 8 byte pointer equal to a static chunk of memory where
| the "Hello" string is pooled.
| FrustratedMonky wrote:
| This is the biggest hurdle for a lot of people, at least
| for me. The immutability.
|
| IF you have grown up doing objects, or C#, and thinking
| with variables. Then 'a=1' means a memory for variable a
| has a 1, and you should be able to change that. But it is
| really a like a function where the function returns a 1.
|
| 'let a = 1' is not assigning the value 1 to variable a.
|
| 'a' is a function that returns a 1.
|
| I think this is biggest reason why people trying to learn
| functional programming in languages that don't enforce
| immutability, have a harder time than with languages that
| do enforce it.
|
| Like moving to another country, and the people around you
| purposely don't speak English so you have to learn the
| language. If the did speak English to help you, then you
| wouldn't learn the language.
|
| Enforcing immutability is like this.
| shortrounddev2 wrote:
| I got the concept of immutability, but I inevitably ended
| up recreating OOP patterns whenever I tried working with
| F#. I do Asp.net WebAPI Projects and trying to do
| dependency injection ends up just translating C# to F#
| code rather writing idiomatic F#. I just don't know if
| HTTP is something F# is really good at doing. I can see
| it being really useful if you were writing something
| math-heavy
| tremon wrote:
| F# is severely hampered by its standard library, which is
| object-oriented by design. Some newer things like fluent
| api's or object initializers are more amenable to F#'s
| functional style, but on the whole my experience matches
| yours: as soon as you start interfacing with any .net
| library, it feels like you're writing in a foreign
| language.
| FrustratedMonky wrote:
| ah. yeah, if you are doing web programming. then for F#,
| you would use a library that is really like a DSL for the
| web. One of the things in practice in F#, it seems the
| most useful libraries are written as DSL's, so it is like
| a language extension for doing 'whatever'. To really get
| these web based ones, need to understand "ELM
| architecture".
|
| In any case. I get it. If you have done ASP.NET, and C#,
| then suddenly this F# way of building web pages by
| programming through combining functions, it is hard to
| get over the hump. Like brain has to re-change how to
| think through the whole flow. Really, F# on the web is
| like ELM.
|
| I like https://websharper.com/
|
| but other use this https://suave.io/
| WorldMaker wrote:
| Giraffe is another interesting one to explore:
| https://giraffe.wiki/
|
| Giraffe is nice because it is itself built "just" as
| ASP.NET Core Middleware so it plays a bit more nicely
| than Suave with a mixed stack of C#-defined Middleware.
|
| It's more likely you accidentally fall back into just
| translating C# patterns to non-idiomatic F# with Giraffe,
| but it's also nicer when in that case of needing to live
| in both worlds and use a mixture of libraries built for
| C# ASP.NET projects.
| xupybd wrote:
| I make web apps with the SAFE stack. Using idiomatic F#,
| the experience has been great.
|
| I can imagine that trying to translate C# to F# would be
| horrible. That is going against the grain.
|
| I'd encourage you to have a look at the SAFE stack it's
| really nice to use. https://safe-stack.github.io/
| phillipcarter wrote:
| > I just don't read code as I would a mathematical proof.
|
| That's fine, although I'd say that FP is probably just not
| for you. It's very much a style of programming that lends
| itself more towards "programming is akin to proofs" than
| "programming is about manipulating things on a von neumann
| architecture". Neither is an incorrect view of the world,
| but they do represent different ways of reasoning about
| things and it's better to use a language more suited
| towards one way of thinking.
| trealira wrote:
| To be honest, when I read e.g. Haskell code, I don't get
| the sense of a mathematical proof at all, not compared to
| proof assistants like Isabelle/HOL, Coq, or Lean. It
| looks more like abstract, high-level wizardry, casting
| that transform the output of one function into the
| correct type so that it can become input into another;
| and yet it's very abstract typing, nothing like proof-
| assistant tactics (though I admit I'm not all that
| experienced with proof assistants either).
|
| I think immutability is more about making it easier to
| reason about the code than mathematical proofs
| specifically.
| trealira wrote:
| > When I read "int a = 1" in C#, I implicitly translate
| that to "take a 4 byte piece of memory on the stack, and
| set it equal to 1".
|
| I think that's overspecifying a bit. It could be kept in a
| register rather than the stack. And due to to the Single
| Static Assignment transformation that modern optimising
| compilers do, variables don't correspond exsctly to
| registers anymore; each time you modify the variable, it
| becomew a new variable, and then the compiler removes or
| changes extraneous modifications and dead code. It only
| keeps track of the values that move through the code. You
| could really only count on variables corresponding exactly
| to stack space registers before the SSA form existed.
| xupybd wrote:
| Regarding the syntax complaints, this is because F# is
| functional first. Using mutable variables is hard while using
| immutable ones is easier.
| daxfohl wrote:
| I felt the same, but once you get used to the syntax, it's much
| nicer for functional first coding. If you find yourself
| fighting the syntax a lot, you're probably using it in an
| imperative style that is not idiomatic in f# anyway.
| derefr wrote:
| Lines 1 and 2 there don't mean different things. They're both
| attempting to bind/unify `a` with a value. It's just that in
| the second case, `a` already _has_ a _different_ value, so the
| binding fails.
| galkk wrote:
| The example with <- is not an assignment, it is called
| shadowing and introduces new value with same name, old one
| isn't changed
|
| But I agree with some ad hoc words criticism
| gsuuon wrote:
| It is indeed assignment: https://learn.microsoft.com/en-
| us/dotnet/fsharp/language-ref...
|
| shadowing would be a second 'let a = 2'
| FrustratedMonky wrote:
| F# is the Scala for .NET.
|
| What about it is lacking?
|
| I'm asking because at one time I was weighing moving to Scala,
| but the F# algebraic types and compiler seemed more complete.
|
| Is it syntax? that does take some getting used to.
| shortrounddev2 wrote:
| I meant the syntax, yeah
| hardlianotion wrote:
| In what way are the algebraic types more complete in F# than
| Scala? I was interested in F# much earlier in the day, but
| that was when Mono was not really a thing and I was not
| really going to go back to Windows.
| louthy wrote:
| > but I just want something closer to Scala, but for .Net
|
| That's what I have been working toward with my language-ext
| library [1]. Including a ZIO like effects system [2], Haskell-
| like Pipes [3], Clojure-like concurrency primitives [4], the
| fastest immutable data-structures in .NET [5], and lots of
| other common FP bits.
|
| Obviously more support for expression based programming would
| be welcome (and higher kinds), but you can do a lot with LINQ
| and a good integrated library surface.
|
| [1] https://github.com/louthy/language-ext
|
| [2] https://louthy.github.io/language-
| ext/LanguageExt.Core/Effec...
|
| [3] https://louthy.github.io/language-
| ext/LanguageExt.Core/Effec...
|
| [4] https://louthy.github.io/language-
| ext/LanguageExt.Core/Concu...
|
| [5] https://louthy.github.io/language-
| ext/LanguageExt.Core/Immut...
| tablloyd wrote:
| You can make the C# code much more F# like with records and a
| switch expression.
|
| Eg:
| https://sharplab.io/#v2:EYLgZgpghgLgrgJwgZwLQAUEEsC2UECeAwgP...
| sbelskie wrote:
| FWIW, this a switch expression rather than a switch statement.
|
| But in any case I really love this addition to the language but
| the inability to have multi-line or block expression arms is a
| constant annoyance for me.
|
| You can even combine these with the new one line record syntax
| to create a poor man's discriminated union.
| ComputerGuru wrote:
| Your pedantic correction is actually important for another
| reason: the switch _expression_ (unlike the switch
| _statement_ ) is defined at the language level as an
| expression (evaluating to/"returning" a value), which would
| be ok except C# doesn't (at the language level) have a
| void/unit type, meaning the switch statement has to return an
| actual value, limiting the places you can use it compared to
| the F# counterpart (or the match expression from rust, etc)
| to very specific cases, usually those performing an
| assignment.
|
| The workaround for that is the same as the workaround for the
| really lame one line limitation: you need to call a
| (preferably (static) local) function in the handler portion
| and then return something like `true` assigned to a discard.
| Hacks all around!
|
| Eg
|
| _ = foo switch a when ... => CaseA(foo), _ => CaseB(foo);
|
| With CaseA and CaseB returning bool in order to call a
| function depending on the value of foo rather than assign a
| value.
| jcpst wrote:
| Microsoft has a good tutorial that showcases what you can do
| when designing with switch expressions in mind:
|
| https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals...
|
| Probably one of my favorite recent-ish additions to the
| language.
| rjbwork wrote:
| The fact that they've added all of this great stuff and have
| STILL not added real discriminated unions is a damn travesty.
| It would just so drastically improve the language.
| jjtheblunt wrote:
| genuine misunderstanding : what do you mean by "real"
| discriminated unions.
|
| I believe I am overlooking something (probably obvious even
| to me) since i know:
|
| https://learn.microsoft.com/en-us/dotnet/fsharp/language-
| ref...
|
| is perhaps a misuse of the term in this context.
|
| (I'm guessing you mean more like in Rust, but am not sure.)
| Smaug123 wrote:
| The parent comment means that C# lacks DUs, not that F#
| lacks them.
| WorldMaker wrote:
| Given few people anticipated ValueTuple and C# adding a
| more direct tuple syntax, I feel like it is only a matter
| of time before C# adds discriminated unions.
|
| (There are multiple proposals tracking the idea. This seems
| the most comprehensive and "central":
| https://github.com/dotnet/csharplang/issues/7016)
| isanjay wrote:
| Would love to see DU in C#
|
| I think they would add it by C# 14 or 15.
| keithnz wrote:
| you can easily do them, I added a comment on the article
| showing how....
|
| https://github.com/linkdotnet/Blog.Discussions/discussions/
| 7...
| LelouBil wrote:
| It's just like in Kotlin !
| pjmlp wrote:
| Maybe the rest of the .NET world has more important stuff
| to care about?
|
| Anyone that cares so deeply about them can do the work on a
| F# assembly.
| diarrhea wrote:
| I'm not arguing there is more important stuff, but DUs
| would enhance the language on a wide, fundamental level,
| accelerating a lot of other advancements.
| Risord wrote:
| Yea I don't care neither but business seems to care. If
| you listen closely their specs are pretty much bloated
| with DUs.
| keithnz wrote:
| its because they are easy to do in C# without explicit
| support, but the proposal is still in the works, but they
| argue a lot about the syntax and about exhaustive type
| checking for all the edge cases.
| ftcHn wrote:
| Agree.
|
| For now, you can get a reasonable DU via an [external
| library](https://github.com/mcintyre321/OneOf).
|
| [Nick Chapsas Video on
| Usage](https://www.youtube.com/watch?v=7z-xjijYfcI).
| pshirshov wrote:
| Well, I've tried. Interoperability is imperfect and it's painful
| to use both languages in one project.
| kerblang wrote:
| Surprised to see that C# doesn't have an Option/Optional
| type/class yet. Even Java made it over that hump.
| pharmakom wrote:
| C# has basically zero value once you learn F#.
| oblio wrote:
| IDE support? Library support?
| pharmakom wrote:
| They are largely the same between F# and C#
| tester756 wrote:
| except projects and job postings count?
| pharmakom wrote:
| I'm only talking about the part where you solve a problem
| with code.
| i_s wrote:
| I'd argue .NET isn't actually that great of a multi language
| platform. We have a mixed F# and C# solution at work, and that
| split sometimes causes a lot of friction. If you have some C#
| code that depends on F# code, that constrains your ability to
| have F# code depend on C# code. You need to be very careful about
| how you organize your work to get a good solution (more than on
| other platforms.)
|
| In the JVM world, mixing languages works a lot better, because
| you can compile .class files instead of whole assemblies. So
| mixing clojure and Java, for example, is very easy to do in any
| order.
___________________________________________________________________
(page generated 2023-08-07 23:00 UTC)