Hacker Newsnew | past | comments | ask | show | jobs | submit | more hither_shores's commentslogin

> Imagine I want to test a function that operates on quite large application state but not all app state is necessary for that function

If not all app state is necessary for that function, it shouldn't require the whole app state in its arguments.

     type AppState = { databaseConnection: DatabaseConnection, env : "dev" | "prod", apiToken: string, userId: string, ... } & SomeOtherStuff

     const dropTables = (app: Pick<AppState, "databaseConnection"> & {env: "dev"} ) => app.databaseConnection.dropTables()
Conversely, if a function you don't control says it needs the whole app state, believe it.


Java and its consequences have been a disaster for the human race


> You'll need sendToUnverifiedEmail(email: UnverifiedEmail) and sendToVerifiedEmail(email: VerifiedEmail), and have code to get the right type to pass to the right function the in the right circumstance...

Only if you're using a language with an insufficiently strong type system (e.g. Java, C#)

in typescript:

    type UnverifiedEmail = { address: string, verified; false }
    type VerifiedEmail = { address: string, verified; true }
    ...
    type Email = UnverifiedEmail | VerifiedEmail | FooBarBazEmail

    const sendToEmail = (email: Email): Promise<void> = ...
in Haskell:

    class SendTo t where
         sendTo :: t -> IO ()

    newtype Email = Email string

    instance SendTo Email where
         sendTo (Email address) = ...

    newtype VerifiedEmail = VerifiedEmail Email deriving (SendTo)
    newtype FooBarBazEmail = FooBarBazEmail Email deriving (SendTo)
> I.e., due to a bug, a value of type VerifiedEmail could be created for an email address that is not really verified. Then, your static checks for VerifiedEmail don't help you at all.

Of course they do - they tell you that the bug is in the verification code, and not in any of the thousands of lines of business logic separating it from the place where the error was found.


I don't know Haskell, but in the typescript code the types aren't doing anything... sendToEmail sends to any kind of Email. And if the code wants to know if an Email is verified, it inspects the verified field.

> Of course they do - they tell you that the bug is in the verification code, and not in any of the thousands of lines of business logic separating it from the place where the error was found.

Whether you centralize the verification code (or otherwise have a separation of concerns for it) or not is independent of whether you use the type system to express when an email is verified or some other mechanism.


> sendToEmail sends to any kind of Email.

That's my point, you don't need a combinatorial explosion of behaviors for every possible most-specific-type, you can just reuse existing ones.

> And if the code wants to know if an Email is verified, it inspects the verified field.

That's exactly what you shouldn't do. Runtime "type" inspection is just bad overly distributed parsing. The point of typing is to move errors to compile-time.

    emailLaunchCodes = (email: VerifiedEmail): Promise<void>  => ...
This doesn't need to inspect anything, because the type signature guarantees that someone else has already handled that.

> Whether you centralize the verification code (or otherwise have a separation of concerns for it) or not is independent of whether you use the type system to express when an email is verified or some other mechanism.

Separating the verification code - which is table stakes, really - doesn't guarantee that you're not accidentally adding or losing "verification" elsewhere. Types can and should.


> The point of typing is to move errors to compile-time.

You've got to understand: Whether or not a particular email address is verified or not isn't something that's generally known at compile time... That means compile time checks cannot actually guarantee if that email address is verified or not.

The compiler cannot know something that isn't known. There must be runtime code somewhere doing the check.

What people are really talking about with types here is that if you organize your code in a certain way, you can put the code that verifies whether an email address is verified or not in one place and use types to help make sure other code doesn't accidentally ignore or change that guarantee.

That's good and fine. But... (1) you can do the very same thing without types; (2) Either mechanism is only as good as your code organization and controls that ensure there's a single place this is determined (and that that place is correct).


> Whether or not a particular email address is verified or not isn't something that's generally known at compile time

Yes, obviously. What is knowable at compile time is the stuff that comes after: given that the input to this function has property X, does the output have property Y? Arguments, not premises.

> But... (1) you can do the very same thing without types

Sure, and if someone makes an SMT-solver-oriented language (that isn't just using it to drive type inference) I'll be happy to try it out. But what most people who dislike types claim is that you can replace them with testing (true in principle, false in practice: no one actually remembers to test every last edge case every single time) or just programmer discipline (lol).


I like types just fine. They are great for expressing static constraints.


Yes, but the mapping doesn't change the relationship between the units of measure, which is the actual meaning as far as the type system is concerned. It's just a change of names.


Sure, I wasn't meaning to detract from the comment above, just to point out what I thought was an interesting related fact.


> this is a classic case of not needing more types but needing proper names.

Those are types.


no. a type is a description of a set of values and its associated operations. Types impose global meaning on entities in your program. When something belongs to a certain type receivers of arguments of that type lose control over how to interpret them. Thus types introduce coupling.

Names are just labels attached to an entity for the purpose of identification and readability, they don't impose meaning.


     newtype ArbitrarilyLabeled x = ArbitrarilyLabeled x
     
     forgetLabel :: ArbitrarilyLabeled x -> x
     forgetLabel (ArbitrarilyLabeled x) = x
What's the "global meaning" of `ArbitrarilyLabeled`? What control has `forgetLabel` lost?


> but I don't think e.g. Haskell98 can do it out of the box in an analogous way?

This is basically the runtime representation of `data Email = Verified { email :: string } | Unverified { email :: string }`, but promoting `Verified` and `Unverified` to type level requires an extension:

    data Email (verified :: bool) where
         Verified :: string -> Email true
         Unverified :: string -> Email false


> That you introduce more coupling is a tradeoff. That the program (sometimes) gets harder to change is a tradeoff.

You don't introduce more coupling, you document the coupling that already exists. If your program is hard to change with types, it would be hard to change without types - but easier to change incorrectly.

> That is becomes easier to write large, messy programs because programmers feel more safe in the future to refactor, is a tradeoff

Sure, I guess this is true in principle - but you could say the same about IDEs, or version control, or grep. The effect size is small.


> You don't introduce more coupling, you don't the coupling that already exists.

This is true at the code level. But at the system-design level, this documentation is the extra coupling.

I feel like it's important to understand this. I agree with the original commenter; in engineering, nothing is truly free. In many cases, this extra coupling helps keep a system strong and stable, like extra nails holding planks of wood together. In other cases, you may find that part of a system's spec actually missed the mark and now needs to be ripped up and redone. That extra coupling might now work against you!

Again, that doesn't mean that it wasn't worth having it. It is just important to understand tradeoffs in engineering.


> since the shared libs have generics and I'm always casting things.

This indicates to me that you're trying to write code that isn't correct (not doesn't work, but rather only works because of implicit couplings between components) and/or doing exotic lisp-style metaprogramming.

In the latter case, yeah, C#'s type system isn't powerful enough. Others are (to an extent: arbitrary code execution at compile time is never going to be completely safe).

In the former case ... that should be difficult. Forcing you to be explicit is half the point of a type system.


Casting is often necessary for parsing inbound data from certain mysql libraries or CSV or JSON depending on how it's written. I would guess that might be what the parent is talking about. That said, if you don't cast or parseFloat or whatever in JS you're going to have a lot of trouble. And if you're doing that, why not do it in Typescript where you'll know that the data you're accessing has been safely cast based on its type.


> Casting is often necessary for parsing inbound data from certain mysql libraries or CSV or JSON depending on how it's written.

No, that's what sum types are for.


I don't see how they're mutually exclusive. I use union types prior to typeguards to end up with ultimately checked, cast values. Say I have a boolean fetched from JSON as "1" or "0". By the time I expose it to the rest of the code as part of a Record, I want to change it to an actual boolean. At first I'm going to treat the inbound value as a union type, e.g. (String | Number | null | undefined | Error). After I deal with the error cases, I'm going to cast it (or in TS, reinitialize it as a single type, boolean) so that any code looking at the imported value sees it as definitely only a boolean without needing to have lots of different pieces of code run their own checks on its type.


I wouldn't really call that casting, that's just narrowing based on control flow.


I mean casting to get mocks to work in unit tests, not live code.

Probably there's a better way. I'm not a C# expert. But I was a Java dev for 10 years so it's not super unfamiliar.


It was a leveraged buyout


Ok, so what if it was a leveraged buyout? All buyouts are. How did Twitter get "saddled" with debt?


No, all-cash buyouts are also a thing.


This was an all-cash buyout. It does not mean it was not leveraged.

All-cash buyouts are those buyouts where the acquirer pays with cash and not their own stock. In this case, Musk used only cash.

But some of that cash (about $12.7 BN) was borrowed from banks. So, it was leveraged.

In principle some buyer like Musk could buy without any help from banks, but why would they do that? You don't get to be a multi-billionaire without getting familiar with financing.

In any case, leveraged or not, Musk did not saddle Twitter with any debt. Maybe he will do it in the future, but for the time being he didn't, because he did not have time to do it.

He could try to issue a huge amount of debt later, and pay himself an extremely fat dividend, so he can turn a quick profit. But in the current market environment, that does not seem even remotely likely. Who is going to buy newly issued Twitter bonds? At what yields? A one year US Treasury yields 4.8%, what type of yield would one require for a bond issued by Twitter? 10%? 15%? If investors get a hint that Musk is just trying to run Twitter into the ground and get himself a nice exit, they won't invest even at 30%. After the whole saga with Musk repeatedly changing his mind on the deal (and before that the $420-for-sure Tesla buyout), how many investors do you think would fall for a get-rich-quick scheme perpetrated by a guy who can't actually climb the richest man ladder anymore?


You do not seem to know what you're talking about. The 12.7bn that was borrowed is going on Twitter's books. Their debt load prior to this leveraged buyout was 5.5bn; it's now 18.3bn[0], with yearly interest payments of roughly 1bn.

[0]: https://www.barrons.com/articles/tesla-stock-twitter-debt-51...


>yearly interest payments of roughly 1bn

This is on top of the principal? They pay 1bn every year and none of it counted against their debt?


That's about right.

1bn/18.3bn = 5.4%

"Risk free" treasury bond yields are ~4.x%

1% premium for the risk seems reasonable given that Twitter is not known for making profits. Presumably some of the debt were incurred before the rate hikes so it's kind of on the low side.


Yes, that's just for the interest.


You may need to change your username. The debt is assumed by Twitter and it is liable for $1bn a year in interest payments:

https://www.nytimes.com/2022/10/30/technology/elon-musk-twit...


Haha, I'll give that serious consideration.

Now, I read that NY Times article, and I guess the crucial sentence that you are referring to is

  To do the deal, Mr. Musk, the world’s richest man, loaded about $13 billion in debt on the company, which had not turned a profit for eight of the past 10 years.
Now, I'm not privy to the deal's details, but since you appear to be more informed, maybe you can clarify something for me.

How could Musk load with debt a company that he did not own yet? Legally, how does this work? In the end someone has to sign a piece of paper. Can I sign a piece of paper making you owe some money to someone else?

I can see how this could work. The then shareholders decided (indirectly via the board of directors) to borrow the cool $12.7 BN so Musk can use his own money and that debt to buy them all out at twice the fair market price. But then was it Musk who loaded Twitter with debt, or the outgoing directors?


You go to a bank and tell it "I would like to buy this company. It has assets worth $N, which could be put up as collateral for a loan of $(.60 x N)." The bank says "Yes, those assets are worth that much. We will sign a contract with you where we will give you $(.60 x N) and in return, once you successfully purchase the company and it is your property, those assets will act as collateral for that loan."

You take the loan (and probably some other money, since by definition the loan is less than the total value of the company's assets), and you go buy the company. You now own the company, therefore you own the assets, therefore you can use them as collateral, and so: the debt that was yours is now on the company's books. You bought out the company with extra leverage provided by the assets you were acquiring: leveraged buyout.

It tends to fail; LBOs tend to target companies that are struggling somehow, and loading an already-struggling company with a shit load of debt frequently results in a bankruptcy. You're probably familiar with companies that dies this way. (My favorite example for fellow millennials is Toys'R'Us.) If you find this troubling: you are correct. But you have forgotten the really important thing: you (or, in most LBOs, a bunch of private equity ghouls) have gotten very wealthy killing a company, and isn't that reward enough?


So let me recap: you (Musk) get a loan ($12.7 BN) from banks, with the idea that the company you buy will be used as a collateral. Before the sale is closed, you can't put it on the company's balance sheet, because you don't own it. But after that you can.

Wait a second. It's true that Musk now owns the company, but transactions between the company and its (sole) owner are still at arm's length, arent's they?

As a pure coincidence, today I received in the mail the bankruptcy ruling for a preschool that went bust and where we have paid $3000 in advance to enroll our daughter. The judge ruled we'll get $1400. But during the lawsuit it transpired that the owner gave himself a nice loan of more than $100k. He had to give the money back (I don't know all the details, although I can find out; I think he settled for a somewhat smaller amount).

Maybe I'm naive, but I think that should happen with larger companies too. Just because you are the sole owner of a company does not mean the company can just lend you money on whatever terms you decide. It's the ultimate conflict of interests.

On the other hand, if the prior shareholders cooperated with Musk, I can see how the debt could be on Twitter's books. But then, it's them who saddled the company with debt, isn't it?


Please read up on the concept of Leveraged Buyouts (LBOs), which were a thing since the 70s and 80s. Private equity LBOs have been a thing precisely because of credit available from banks. Last I checked, PE firms such as KKR are still prosperous, in spite of conducting an LBO of similar scale (RJR Nabisco), after saddling the company with extremely high amounts of debt (for the 80s).

And please don't drive the discussion to tangents and anecdata, even though your analogy is flawed.


It's about the meaning, not the raw bits. I might be able to duplicate your signature, but that doesn't mean I can sign documents for you.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: