kellysmith12.21
2021-2-18 08:12:01

Most Racket programs are written in “untyped” Racket (#lang racket) and contracts are used to enforce program invariants.


kellysmith12.21
2021-2-18 08:13:11

Typed Racket is used when you want/need the compiler to statically check invariants. Additionally, #lang typed/racket has a number of optimizations, which makes it useful for high-performance software.


kellysmith12.21
2021-2-18 08:14:36

The idea is that Typed and Untyped Racket coexist. You can choose which one to use, based on the needs of your project, and you can freely mix typed and untyped code.


kellysmith12.21
2021-2-18 08:16:45

Typed Racket was designed to be very similar to plain old Racket, so it’s easy to work with both in a project and so it’s easy to take an untyped module and change it into a typed module.


kellysmith12.21
2021-2-18 08:17:37

Hence the terms, “Gradual Typing” and, “Migratory Typing”.


kellysmith12.21
2021-2-18 08:20:26

In general, start with plain Racket and use contracts. If you decide later on that you want/need static checking, then you can incrementally switch modules over to Typed Racket.


kellysmith12.21
2021-2-18 08:23:12

(Disclaimer: I’m far from an expert on the subject, but there are plenty of people here who are highly qualified to give more detail.)


louis77
2021-2-18 10:31:29

@kellysmith12.21 Thank you so much for your comments. That answers exactly my questions. I’m coming from statically typed languages (Go) and love the safety of it, but it also sometimes makes simple tasks cumbersome, so I fancy the dynamic nature if Lisp. It’s great that Racket has both and that there is no fundamentalism about it.


kellysmith12.21
2021-2-18 10:41:06

@louis77 You’re welcome!


greg
2021-2-18 15:08:32

@louis77 adding to @kellysmith12.21’s great explanation:

  • Contracts are also part of the story of how untyped code can safely call typed code: Typed Racket automatically creates contracts to guard the boundary. The contracts enforce the types.

  • Type checkers like Typed Racket work at compile time. Contracts happen at run time. As a result, a Typed Racket program might take longer to compile but generally run more quickly than some “equivalent” program using contracts. In some parts of some programs, doing runtime checks (like contracts) can be slow enough to be undesirable.

As a result, sometimes people prefer to put contracts only at interesting boundaries of their programs. Maybe just at module boundaries (so the contract is added only with provide and contract-out). Maybe not even modules boundaries — maybe just at edges that input or output data, and/or provided functions that are the “public API” of some library.

TL;DR: Racket gives you choices. Choices have trade-offs.