Skip to main content
added 651 characters in body
Source Link
Rufflewind
  • 2.2k
  • 2
  • 16
  • 19

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your Displayables.


On the issue of default definitions, they should be used cautiously. Additionally, it's probably a bad idea to write defaults that don't work on every instance. It's not a strict guideline, but it really helps to prevent silly mistakes like forgetting to write a function for an instance: without defaults, if you forget something, the compiler will give you a warning, or at worst cause a runtime error; that's not the case if it's defaulted, and if the default behavior is wrong, then your program might end up being subtly broken.


Addendum: a "record of methods" (or "instance dictionary" as it's often called) is actually what Haskell uses to implement type classes under the rug. Such an approach would be something like:

data Displayable d = Displayable
  { display   :: d
  , toPicture :: d -> Picture
  , getDims   :: d -> (Float, Float)
  , getPos    :: d -> (Float, Float)
  , getScale  :: d -> Float
  , setDims   :: (Float,Float) -> d -> d
  , setPos    :: (Float,Float) -> d -> d
  , setScale  :: Float -> d -> d
  }

Any type class can be transformed into this form: this a literal translation of the type class in your question. You probably don't want to use in this exact form though: it's best if you can figure out which parts of Displayable really needs to be abstract, and which parts you can make concrete. Too much flexibility can make the code more complex than it needs to be.

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your Displayables.


On the issue of default definitions, they should be used cautiously. Additionally, it's probably a bad idea to write defaults that don't work on every instance. It's not a strict guideline, but it really helps to prevent silly mistakes like forgetting to write a function for an instance: without defaults, if you forget something, the compiler will give you a warning, or at worst cause a runtime error; that's not the case if it's defaulted, and if the default behavior is wrong, then your program might end up being subtly broken.


Addendum: a "record of methods" (or "instance dictionary" as it's often called) is actually what Haskell uses to implement type classes under the rug. Such an approach would be something like:

data Displayable d = Displayable
  { display   :: d
  , toPicture :: d -> Picture
  , getDims   :: d -> (Float, Float)
  , getPos    :: d -> (Float, Float)
  , getScale  :: d -> Float
  , setDims   :: (Float,Float) -> d -> d
  , setPos    :: (Float,Float) -> d -> d
  , setScale  :: Float -> d -> d
  }

Any type class can be transformed into this form.

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your Displayables.


On the issue of default definitions, they should be used cautiously. Additionally, it's probably a bad idea to write defaults that don't work on every instance. It's not a strict guideline, but it really helps to prevent silly mistakes like forgetting to write a function for an instance: without defaults, if you forget something, the compiler will give you a warning, or at worst cause a runtime error; that's not the case if it's defaulted, and if the default behavior is wrong, then your program might end up being subtly broken.


Addendum: a "record of methods" (or "instance dictionary" as it's often called) is actually what Haskell uses to implement type classes under the rug. Such an approach would be something like:

data Displayable d = Displayable
  { display   :: d
  , toPicture :: d -> Picture
  , getDims   :: d -> (Float, Float)
  , getPos    :: d -> (Float, Float)
  , getScale  :: d -> Float
  , setDims   :: (Float,Float) -> d -> d
  , setPos    :: (Float,Float) -> d -> d
  , setScale  :: Float -> d -> d
  }

Any type class can be transformed into this form: this a literal translation of the type class in your question. You probably don't want to use in this exact form though: it's best if you can figure out which parts of Displayable really needs to be abstract, and which parts you can make concrete. Too much flexibility can make the code more complex than it needs to be.

added 651 characters in body
Source Link
Rufflewind
  • 2.2k
  • 2
  • 16
  • 19

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your Displayables.


On the issue of default definitions, they should be used cautiously. Additionally, it's probably a bad idea to write defaults that don't work on every instance. It's not a strict guideline, but it really helps to prevent silly mistakes like forgetting to write a function for an instance: without defaults, if you forget something, the compiler will give you a warning, or at worst cause a runtime error; that's not the case if it's defaulted, and if the default behavior is wrong, then your program might end up being subtly broken.


Addendum: a "record of methods" (or "instance dictionary" as it's often called) is actually what Haskell uses to implement type classes under the rug. Such an approach would be something like:

data Displayable d = Displayable
  { display   :: d
  , toPicture :: d -> Picture
  , getDims   :: d -> (Float, Float)
  , getPos    :: d -> (Float, Float)
  , getScale  :: d -> Float
  , setDims   :: (Float,Float) -> d -> d
  , setPos    :: (Float,Float) -> d -> d
  , setScale  :: Float -> d -> d
  }

Any type class can be transformed into this form.

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your Displayables.


On the issue of default definitions, they should be used cautiously. Additionally, it's probably a bad idea to write defaults that don't work on every instance. It's not a strict guideline, but it really helps to prevent silly mistakes like forgetting to write a function for an instance: without defaults, if you forget something, the compiler will give you a warning, or at worst cause a runtime error; that's not the case if it's defaulted, and if the default behavior is wrong, then your program might end up being subtly broken.

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your Displayables.


On the issue of default definitions, they should be used cautiously. Additionally, it's probably a bad idea to write defaults that don't work on every instance. It's not a strict guideline, but it really helps to prevent silly mistakes like forgetting to write a function for an instance: without defaults, if you forget something, the compiler will give you a warning, or at worst cause a runtime error; that's not the case if it's defaulted, and if the default behavior is wrong, then your program might end up being subtly broken.


Addendum: a "record of methods" (or "instance dictionary" as it's often called) is actually what Haskell uses to implement type classes under the rug. Such an approach would be something like:

data Displayable d = Displayable
  { display   :: d
  , toPicture :: d -> Picture
  , getDims   :: d -> (Float, Float)
  , getPos    :: d -> (Float, Float)
  , getScale  :: d -> Float
  , setDims   :: (Float,Float) -> d -> d
  , setPos    :: (Float,Float) -> d -> d
  , setScale  :: Float -> d -> d
  }

Any type class can be transformed into this form.

added 550 characters in body
Source Link
Rufflewind
  • 2.2k
  • 2
  • 16
  • 19

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your DisplayablesDisplayables.


On the issue of default definitions, they should be used cautiously. Additionally, it's probably a bad idea to write defaults that don't work on every instance. It's not a strict guideline, but it really helps to prevent silly mistakes like forgetting to write a function for an instance: without defaults, if you forget something, the compiler will give you a warning, or at worst cause a runtime error; that's not the case if it's defaulted, and if the default behavior is wrong, then your program might end up being subtly broken.

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your Displayables.

It's usually a good idea avoid type classes unless it provides a specific benefit that cannot be gained by any other means. This article provides a somewhat extreme view on this, so take it with a grain of salt, but it can help you understand why type classes can be troublesome.

The main issue type classes operate on the type level, which means that trying to do complicated things with type classes will typically require a plethora of Haskell extensions, and sometimes even impossible. in contrast, operating on the value level is much simpler and you don't have to fight with the type checker and type inferencer regularly.

Getters and setters have their places in Haskell, but they are primarily for maintaining interface compatibility and using them excessively will cause a lot of boilerplate. For simple, internal data structures, they might not be worth the extra effort.

In your example, it would be much simpler to implement Displayable as an ordinary data type (as shown in Karl Bielefeldt's example), with the added benefit that you can store all your Displayables in a container without funky extensions like ExistentialTypes. Additionally, you can make use of pattern matching (or the record syntax) to deconstruct your Displayables.


On the issue of default definitions, they should be used cautiously. Additionally, it's probably a bad idea to write defaults that don't work on every instance. It's not a strict guideline, but it really helps to prevent silly mistakes like forgetting to write a function for an instance: without defaults, if you forget something, the compiler will give you a warning, or at worst cause a runtime error; that's not the case if it's defaulted, and if the default behavior is wrong, then your program might end up being subtly broken.

Source Link
Rufflewind
  • 2.2k
  • 2
  • 16
  • 19
Loading