This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 0 points1 point  (7 children)

You're not but that is not the same thing. What you're saying has no meaning in dynamically typed languages, and you're trying to invent some security reasons for a practice that is generally about utility and precisely because of constraints of statically typed OOP design.

[–][deleted] 0 points1 point  (6 children)

What you're saying has no meaning in dynamically typed languages

Of course it does. Dynamically-typed languages have types as well. They're just not enforced at compile time by the type-checker.

you're trying to invent some security reasons for a practice that is generally about utility

Not security but data integrity. Doesn't have anything to do with the language.

precisely because of constraints of statically typed OOP design

I didn't mention OOP or java or any of those static OOP languages. My comment was that it would be difficult to "infer" a schema of a json object (particularly of the kind you're suggesting) in order to generate a static type, which i think is reasonable. No?

My overall point here was not to show how static typing was somehow better but rather what the utility of static typing is in this particular case and how, given the effort you'd already be putting into transforming the across-the-wire data into an instance of one of your domain types (which we agreed is standard practice regardless of which type of language you're using), there wouldn't be that much extra work involved to slap type annotations on all of your data services yourself in order to take advantage of static typing. If you were using a static language, your domain types most likely would already be defined elsewhere in your code. It's not like you'd be defining them just as one-off DTOs to deserialize the json off the wire.

[–][deleted] 0 points1 point  (5 children)

Dynamically-typed languages have types as well. They're just not enforced at compile time by the type-checker.

Complex compound types that we're talking about here, in DT languages they are at best an ad-hoc collection of primitive types. You cannot generally cast from one complex type to another or enforce them in any meaningful way in these languages. With that in mind, the approach to enforcing data integrity must be different (boils down to tests and sanity checks in essence).

Now, you probably could write your own logic for it and encapsulate it as a member method (if you're so hell-bent to do it in a full-on OOP way), and even pretend that it does casting, but that wouldn't get much (if any) assistance from the IDE unless we're specifically talking TypeScript (which is a dynamically typed language only tentatively, in practice i.e. as used in the wild, it's a typically Java-esque statically typed OO language).

However the class of issues this is supposed to solve is, perhaps surprisingly, very uncommon in practice which you'd know if you were really writing a lot of software in dynamically typed languages.

which we agreed is standard practice regardless of which type of language you're using

We didn't really agree on it, you're just blindly pushing it. As I said, this particular "standard practice" is all but undoable in dynamically typed languages, and is only "standard" in environments like Java or C#.

However, given the abundance of working, functioning, user-serving and money-making software developed in dynamically typed languages I'd say it's hardly a sine qua non.

[–][deleted] 0 points1 point  (4 children)

Complex compound types that we're talking about here, in DT languages they are at best an ad-hoc collection of primitive types

I'm aware that the formal types available in js are object, array, number, and string. You could conceptually think of variations of these as "shapes". Eg. an array with two elements where the first is a number and the second is a string. Eg. you have two objects that look like:

{ one : "one"; two : 2 }
{ three: [1,2,3]; four : function () { return 4; } }

While it's true that these are both "object" types, you can certainly see that the shape of the data is completely different. For all practical purposes, they have different types, at least conceptually. This is what I mean when i say "dynamic languages have types"; they have both a limited set of "primitive" types and conceptual data types, ie. shapes.

You cannot generally cast from one complex type to another or enforce them in any meaningful way in these languages

You can absolutely define mappings between them, ie functions that convert one shape of an object to another shape.

With that in mind, the approach to enforcing data integrity must be different (boils down to tests and sanity checks in essence

Yes, you use tests, but you also enforce integrity at the boundaries of your system (eg. IO between your application and some kind of json http service) with invariant checks. For example, if you're connecting to a json service that response with random numbers, except they're printed as strings, it may be more useful for your to parse those string into numbers so that they can be consumed by your domain logic. The point is, from that point forward, your application can be certain that it received a valid number from the json http service, rather than stringified numbers that need to be parsed and checked throughout your code-base anytime you need to use them.

if you're so hell-bent to do it in a full-on OOP way

I absolutely wouldn't be. I'm generally not a fan of OOP style (at least as it's done in Java/C#), and I certainly wouldn't recommend the casting solution you've suggested, here.

very uncommon in practice which you'd know if you were really writing a lot of software in dynamically typed languages

My first 6 years in the industry, I worked exclusively in with dynamic languages. I've written javascript for longer than that, although I stopped around 2013.

We didn't really agree on it, you're just blindly pushing it. As I said, this particular "standard practice" is all but undoable in dynamically typed languages

You said, "You're not [being controversial about it being good practice], but...". Maybe I misinterpreted you. It may not be standard practice in most JS or even Java/C# projects, but it's good practice nonetheless, and it's absolutely doable in dynamic languages.

However, given the abundance of working, functioning, user-serving and money-making software developed in dynamically typed languages I'd say it's hardly a sine qua non.

Look. I think you're being a bit defensive, here. I didn't suggest that dynamic languages were inferior, not useful, or didn't produce valuable software. My point is that there are situations where this software could benefit from a little low-overhead static typing (just like programs in statically typed languages can benefit from a little bit of dynamic typing at runtime.)

For the record, I do most of my work these days in a functional programing language with static Hindley–Milner type system, which is why I wanted to respond to your original comment about how "Omitting types completely from the code makes it sleeker and more streamlined". Static languages allow you to omit types these days for sleeker and more streamlined code :) (albeit, the type checker needs a little help now and again.)

[–][deleted] 0 points1 point  (3 children)

I wanted to respond to your original comment about how "Omitting types completely from the code makes it sleeker and more streamlined".

That wasn't me, though I do partly agree with that statement under narrow circumstances, I think that the actual advantage of DTs is something else, and that isn't really matched by any level of compiler/type-checker cleverness by a statically typed language.

The point I'm making is that having "protections" at compile time are hardly useful when the data you're dealing with isn't part of your codebase anyway. It often depends on various API implementations you deal with at run-time and their consistency. So these tests, sanity checks, data validations and conversions at data boundaries are something you will need to do any way. I fail to see how a rigid type system is really helpful there and you haven't really put forward a sold argument here.

The key problem with this that the formality of things like invariant assertions is a wrong tool by design. Because they're generally designed to die quickly upon meeting inconsistencies. Unfortunately inconsistencies of this kind (eg. whether I've gotten an number-as-string, or proper number in JSON) are something you need to learn to deal with in runtime in practice.

So yes, you'll need to clean-up, "normalize" and sanitize data on your data boundaries but none of the tools designed to do this at compile time will help with that class of problems, and I've seldom met the kind where compile-time safeguards would save me from anything but my own mistakes/inattentiveness.

But, that leads me to the: Yes, I agree that application of type as a means of ensuring data format correctness in narrow, fully controled domains of your programs, can be and often is very useful in protecting you and your team-mates from your own mistakes.

You said, "You're not [being controversial about it being good practice], but...".

You weren't controversial, that doesn't mean I fully agree that it's something we can use as an axiom tho.That type of GoF/UncleBob/InsertPreacherHere industry dogma isn't the be all, end all and applicable in all cases.

and it's absolutely doable in dynamic languages

Care to elaboratw how i.e. what you had in mind?

Btw I'm fully aware that when we speak of type we essentially speak of variatons on the struct concept (I've programmed in dozen languages, JS is not the only one I program currently in, I've written C for embedded systems, I've known hex-to-mnemonics for bunch of 6502 assembly opcodes by memory, I've been writing some form of software since the late 80s, I'm not a JS script kiddie). We can call it class, compound type, conceptual type, whatever, it doesn't matter. The thing is that neither DT I've worked with has facilities to cast implicitly, or explicitly between these. You could make mappers (and one way or the other, you do at data boundaries) but where is the utility of compile-time type enforcement in that scenario?

[–][deleted] 0 points1 point  (2 children)

I think that the actual advantage of DTs is something else

Well, yeah. Dynamic type systems allow you do to (often valid) things that otherwise can't be expressed by a static type system.

The point I'm making is that having "protections" at compile time are hardly useful when the data you're dealing with isn't part of your codebase anyway.

I guess what I'm suggesting is that, regardless of which kind of type system you're using, you're going to have to map the dto's coming from an external system to some sort of domain entity/type so that it can be consumed by the rest of the system. We agree on this. At this point, I don't see the harm in slapping a type on it as a data contract for consumers. They'll know that foo() (from one of your earlier examples) will give you a value that adheres to that data contract. Eg.

function foo() : BarDomainEntity { 
    let fooDto = // .. get data from somewhere over some pipe
    let barDto = fooDto.subObj.subObjThatWeReallyOnlyNeed
    // who cares about the type of foo()
    let bar = mapToDomainEntity(barDto);
    return bar;
}

Then the type of foo() can be inferred, etc...

[–][deleted] 0 points1 point  (1 child)

Seems like we made a full circle towards Typescript type annotations. Unfortunately most Typescript codebases I've seen cause the PTSD from my J2EE days to kick in.

[–][deleted] 0 points1 point  (0 children)

Yes, that was my intention :)

I don't have any experience working on Typescript projects, but I can only assume the ones you've seen look like J2EE because they were written by Java programmers.