Live data from Hacker News

Types as Interfaces

two-wrongs.com

141–150 of 197 posts

Re: Types as Interfaces

#141

Earlier quoted context omitted.

> In the string -> Email example, it's probably enough to parse your string and just call it an email. You don't need to try to encode all the rules about an email into the type itself. There is also the in-between Rust approach. Start with the user input as a byte array. Pass it to a validation function, which returns it encapsulated within a new type. #[derive(Clone, Hash, Ord, Eq...)] struct Email(Box ); // valida…

AKA the "parse, don't validate" approach [1]. 1: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

The salient excerpt is: "A parser is just a function that consumes less-structured input and produces more-structured output". In opposition to a validation function that merely raises an error.

    - validate(raw) -> void
    - parse(raw) -> Email | Error
Of course the type signature is what seals the deal at the end of the day. But I am going to follow this naming convention from now on.

Re: Types as Interfaces

#142
post #129

Earlier quoted context omitted.

Sometimes I’ve wondered if versioned types might be a help in bringing coexistence to a typed setup, and if this is part of where the benefits of a service oriented architecture come from. But given how versioning snarls can play out in dependency management I expect this idea has some tradeoffs in the best case.

I'm curious what you have in mind that doesn't boil down to "duck typing?"

Not the most elegant mechanism, but it should be accessible to imagine an implementation via inheritance hierarchies even for a static manifestly typed language. Class "Person4.1" inherits from class "Person4" inherits from class "Person2" inherits from "Person". Probably there's a better way (and one could argue that a design where knowing the version of the class/object matters isn't a well-encapsulated design, which could have something to do with why duck typing often works out better than expected in OO systems).

Re: Types as Interfaces

#143

This is a lot of complication for in what most OOP languages with interfaces would simply be something like: interface Timestamped { timestamp: UTCTime; } interface Msg { sender: PlayerId; } class Quote implements Timestamped, Msg { timestamp: UTCTime; sender: PlayerId; } Why is this so hard in Haskell? It doesn't have interface polymorphism?

It's not hard at all and it's actually what the second approach listed by the author shows:

    class HasRecipient a where
      get_receiver :: a -> PlayerId
which adjusted to your example would be

    class Timestamped a where
      timestamp :: a -> UTCTime
The problem with this approach is that you'll have duplicated data on all instances. In your example, `Quote` has the fields `timestamp` and `sender` in order to satisfy with `Timestamped` and `Msg`. If you had several classes like `Quote` and interfaces like `Timestamped` then you would end up with a lot of duplicated code.

Re: Types as Interfaces

#144
post #23

types are not interfaces. interfaces describe behavior, types describe shape and structure. the difference is subtle but important.

> interfaces describe behavior, types describe shape and structure Shape and structure are behavior. There's more to behavior than "abstractly produces X result". Behavior is "produces X result in Y form given Z in W form".

Shape and structure are behavior.

They shouldn't be. Conflating two things that can be separated is just compounding complexity. You don't need to know how a database lays out memory or the structure behind a web server.

Re: Types as Interfaces

#145

Earlier quoted context omitted.

> interfaces describe behavior, types describe shape and structure Shape and structure are behavior. There's more to behavior than "abstractly produces X result". Behavior is "produces X result in Y form given Z in W form".

Shape and structure are behavior. They shouldn't be. Conflating two things that can be separated is just compounding complexity. You don't need to know how a database lays out memory or the structure behind a web server.

> You don't need to know how a database lays out memory

You do, however, need to know what logical columns are in a table and the types of those columns to be able to effectively query against the table. And you need to know what the types of the inputs to a query wrapper function are to be able to call it properly.

Memory layout has nothing to do with type, because physical memory layout is completely separate from the semantic logical layout and form represented by the bits. You now appear to be the one conflating two unrelated things.

That something is treated as an integer fundamentally matters to its use. That the thing comprises some number of adjacent bits in big/little-endian arrangement is a very unrelated implementation detail.

Re: Types as Interfaces

#146
post #131

Earlier quoted context omitted.

Parsing a string into an email, of course, is already fraught with peril. Is it valid HTML? Or did you mean the email address? Is it a well formed email, or a validated address that you have received correspondence with? How long ago was it validated? Fun spin on the, "It's an older code, sir, but it checks out." I've seen attempts at solving each of those issues using types. I am not even positive they aren't solvab…

Validation is an event, with it's own discrete type separate from the address itself. This is no different than a physical address. 123 Somewhere Lane Somewhereville, NY 12345 is a correctly formatted address but is almost certainly not one that physically exists. Validation that it exists isn't solvable in the type system because, as I mentioned, it is an event. It is only true for the moment it was verified, and th…

I think I get your point. I would add reconciliation to validation. In that sometimes you cannot validate something without doing it, and are instead storing a possibly out of band reconciliation of result.

I'm curious, though, in how this argument does not apply to many other properties people try and encode into the types? It is one thing if you are only building envelopes and payloads. And, I agree that that gets you a long way. But start building operations on top of the data, and things get a lot more tedious.

It would help to see examples that were not toy problems. Every example I have seen on this is not exactly leaving a good impression.

Re: Types as Interfaces

#147
post #77

Earlier quoted context omitted.

I regret to say that every type level Gordian knot that I have ever been exposed to came from attempts to do this. I think a lot of the problem is that many of the constraints and acceptable values for data are not determined until well after first deployment. The "knot" comes in when you are explicitly changing data and code to add a feature that you didn't anticipate at the start. This is a lot like schemaless data…

>> This is a lot like schemaless databases. The flexibility of not having to fully specify the full schema does not mean that you don't benefit from specifying parts of it? Indeed, if you are indexing things, you have to specify those parts. But it is hard to argue with a straight face that schemaless tools don't have some strong benefits. This is very similar to what Rich Hickey argued in "Simplicity Matters": https…

The first two properties can be handled by adding optional fields to your struct. The third can be done at compile time rather than runtime if you define a strict class hierarchy. I think really the problem is that many languages make struct definition needlessly verbose so it feels convenient to just use a map. The price of using schemaless includes runtime performance as well as the compiler being unable to warn you about easy (for a compiler) to detect and report data mismatch errors. I have spent a lot of time using lua the way I think many people use clojure. It is quite seductive and does feel very productive at first, but eventually I find that for a sufficiently complex program, I want the compiler to validate my data usage for me.

Re: Types as Interfaces

#148
post #129

Earlier quoted context omitted.

I'm curious what you have in mind that doesn't boil down to "duck typing?"

Not the most elegant mechanism, but it should be accessible to imagine an implementation via inheritance hierarchies even for a static manifestly typed language. Class "Person4.1" inherits from class "Person4" inherits from class "Person2" inherits from "Person". Probably there's a better way (and one could argue that a design where knowing the version of the class/object matters isn't a well-encapsulated design, whi…

I think my question is what would you be doing here that doesn't boil down to "has these properties/methods, I'll accept"?

I think this is a lot easier if you exclude the "methods" in my quote there. Since those almost certainly have the same general contract that would spread across things.

Re: Types as Interfaces

#149

Earlier quoted context omitted.

>> This is a lot like schemaless databases. The flexibility of not having to fully specify the full schema does not mean that you don't benefit from specifying parts of it? Indeed, if you are indexing things, you have to specify those parts. But it is hard to argue with a straight face that schemaless tools don't have some strong benefits. This is very similar to what Rich Hickey argued in "Simplicity Matters": https…

Protobufs are an interesting hybrid. They assume a common, linear history of schema versions, where all data generators had access to an arbitrary version in that history. The schemas define field ids and types, but not which combinations of fields can show up. If you can upgrade all the data then you don’t need to worry about versioning, Or you can do it like HTTP headers and hope for the best.

Protobufs are also a fun place to look for how much debate people will get into regarding required versus optional fields. Your post is taking an implicit "everything is optional" view. But, it does allow you to be stricter.

Common Lisp Object System also touched on all of these ideas years ago.

Post reply on HN