3 ms·
> In F# the order of the files matter. I didn’t quite understand this section and it seems very counterintuitive can someone elaborate a little?
by have_faith 6y ago
> In F# the order of the files matter.
I didn’t quite understand this section and it seems very counterintuitive can someone elaborate a little?
- danyx 6y agoIn an F# project file (and thus also in all editor support), the referenced F# source files have an order. Files can only see types, values etc defined in files above them (and the same within files, with some caveats). This sounds really weird and annoying but turns out to have some surprising benefits (because it limits the mental/real search space for definitions as well). In practice it's just something that is surprising at first, then totally fine.
- Kinrany 6y agoThis guarantees that there are no cycles, but there are more ergonomic ways to do that. And the change would be backwards compatible. I wish languages didn't insist on preserving quirks like this one.
- scns 6y agoThat is pretty nice for testing, since it removes the need for depency injection, or am i mistaken?
- ben509 6y agoAll DI libraries can be ripped out and replaced with a bog standard main method, and it doesn't even require much skill. Most of what it's doing for you is stuff like "foo = new Foo(); bar = new Bar(foo, 33);" So there's no strict "need" for them, they help with (and enable) complexity. What F#'s aversion to cyclic dependencies does forces you to break cycles of types referring to types referring to types. The way you do that is by writing a generic function that avoids referring to a concrete type. Because it's generic, such a function is inherently easier to test by means of a simple stub value.
- piaste 6y agoNot really. There are various DI patterns you can use in F# [1], but the combination of no cyclic dependencies + type inference does mean you can just do the simplest thing - pass dependencies manually as specific arguments - and it will stay manageable for much longer than in java/c#. Evil dangerous code using global variables: let mail = // .. create email service let db = // .. create database service let receiveThing thing = async { let query = // .. compose update query do! saveToDb db query let emailText = // .. compose email do! sendMail mail emailText } while readInput() do receiveThing (getThing()) Beautiful pure code using dependency injection: let mail = // .. create email service let db = // .. create database service let receiveThing db mail thing = async { let query = // .. compose update query do! saveToDb db query let emailText = // .. compose email do! sendMail mail emailText } while readInput() do receiveThing db mail (getThing()) [1] https://fsharpforfunandprofit.com/posts/dependencies/ https://fsharpforfunandprofit.com/posts/dependencies/
- IshKebab 6y agoI think he means you have to write down a list of files in your project somewhere, but each file can only depend on files that come before it in the list. So the files with the fewest dependencies (e.g. utility type stuff) come first, and `main.fs` (or whatever) comes last.
- tentacles 6y agoYou do it in the .fsproj. I wish Microsoft would add a warning to explain that, instead of just saying the modules can't be found. Really confused me for a good few hours.
- moron4hire 6y agoMy general feeling is that, if you can generate a warning that tells you the right thing to do, then the compiler should just do the right thing.
- Smaug123 6y agoThat works right up until it doesn't. For example, if you have two interacting places where the compiler has guessed what to do, maybe the compiler resolves the interaction in the wrong way. It's the same problem that arises if you don't commit after merging into a Git repository (which, by the way, is possible but takes explicit opt-in): it keeps on working fine up until two previous merges conflict with each other. I much prefer Rust's approach, which is to fail out but also make it extremely easy to apply the fix the compiler has guessed.
- IshKebab 6y agoThat's wrong on two levels: 1. The compiler doesn't usually exactly know the right thing to do. E.g. in this case presumably the error should be something like "You need to order the files so that each file doesn't depend on anything further down the list." The compiler doesn't know what that order should be (maybe in this case it could figure it out, but the point is more general than just this instance). 2. Sometimes the compiler might be able to figure out what you meant but you still did it wrong. If you accept wrong code then pretty soon people will start thinking that it is right, and then you have to support it forever. This is closely related to the thoroughly disproven "Robustness Principle", which actually leads to systems that are really not robust. HTML parsing is a good example (though mostly fixed today).