Coder Social home page Coder Social logo

Comments (62)

avilladsen avatar avilladsen commented on May 28, 2024 125

I also was initially put off by the lack of ADTs in Dart. But while there isn't language-level support, ADTs can be decently represented in Dart. For example, here's how I would translate your Kotlin example into Dart:

abstract class LoginResponse {
  // Not necessary when case classes are public, 
  // but I usually put factories and constants on the base class.
  
  factory LoginResponse.success(authToken) = LoginSuccess;
  static const invalidCredentials = const InvalidLoginCredentials();
  static const noNetwork = const NoNetworkForLogin();
  factory LoginResponse.unexpectedException(Exception exception) = UnexpectedLoginException;
}

class LoginSuccess implements LoginResponse {
  final authToken;
  const LoginSuccess(this.authToken);
}

class InvalidLoginCredentials implements LoginResponse {
  const InvalidLoginCredentials();
}

class NoNetworkForLogin implements LoginResponse {
  const NoNetworkForLogin();
}

class UnexpectedLoginException implements LoginResponse {
  final Exception exception;
  const UnexpectedLoginException(this.exception);
}

And for the when statement:

void on(LoginResponse loginResponse) {
  if (loginResponse is LoginSuccess) {
    // Dart has smart casting too, so you can use authToken w/o a cast
    loginResponse.authToken;
  } else if (loginResponse is InvalidLoginCredentials) {
    
  } else if (loginResponse is NoNetworkForLogin) {
    
  } else if (loginResponse is UnexpectedLoginException) {
    // Dart has smart casting too, so you can use exception w/o a cast
    loginResponse.exception;
  } else {
    // Dart doesn't have sealed classes
    throw new ArgumentError.value(loginResponse, 'loginResponse', 'Unexpected subtype of LoginResponse');
  }
}

Does this satisfy your request for ADTs? I've been using this pattern myself for a while, and at this point I don't think that Dart needs language-level support for ADTs per se (though I wouldn't complain if it was added).

Because Dart does not have sealed classes or a when-like statement, you can have runtime type errors, which is the one thing I'm really not happy with. I'd be in favor of adding those to Dart. However, as has been pointed out elsewhere, those are really a separate, broader issue than ADTs.

from language.

brianegan avatar brianegan commented on May 28, 2024 123

Hey @avilladsen --

The features I would like (whether it's sealed classes or union types):

  1. Exhaustive Checking with automatic casting inside when + is expressions. This provides static analysis to ensure you're handling each and every case. This eliminates the need for some types of tests.
  2. Constrained types. Not open for extension outside of the declaration. This makes reading the code a breeze, since you can see "Ok, this type encapsulates exactly these responsibilities".
  3. Concise syntax without ==, toString, and hashcode and copy boilerplate, although I think "data classes" are a separate issue. For example, none of your classes include these methods, and they need to be implemented for testing to work more easily.
  4. Convenient Domain Modeling: https://skillsmatter.com/skillscasts/4971-domain-driven-design-with-scott-wlaschin

Sure, defining a class with some subclasses will work in combination with is checks, but it's not as safe (lack of exhaustive checking, need to write more tests) and more verbose. I think you can see a great example of modeling state in this fashion here: https://github.com/freeletics/RxRedux#usage

You can see an attempt that some of us in the community have worked on to achieve this: https://github.com/fluttercommunity/dart_sealed_unions

You could also use built_value to generate the ==, toString etc, but that is also difficult to read and requires a build step.

Therefore, we can paper over these issues with libraries, but they're far more cumbersome and much harder to read than workin with sealed + data classes in Kotlin. In fact, I even prefer the Scala version with pattern matching and destructuring: https://docs.scala-lang.org/tour/pattern-matching.html

from language.

ZakTaccardi avatar ZakTaccardi commented on May 28, 2024 94

ADTs can be decently represented in Dart

I'm looking for language level support, unfortunately. A modern programming language should make it easy to represent ADTs.

At least Dart has smart casting

from language.

jarlestabell avatar jarlestabell commented on May 28, 2024 85

I've been developing in Flutter for two weeks and I've been really blown away by the incredibly well thought of framework and surrounding tooling!
I think Flutter is the biggest progress when it comes to developer productivity for UI programming since Visual Basic 1.0 / Delphi 1.0 et al in the mid nineties.

But I really miss the beautiful sum types of the ML-family (OCAML, F#, ReasonML, Haskell etc).
Please compare the Dart example from @avilladsen :

abstract class LoginResponse {
  factory LoginResponse.success(authToken) = LoginSuccess;
  static const invalidCredentials = const InvalidLoginCredentials();
  static const noNetwork = const NoNetworkForLogin();
  factory LoginResponse.unexpectedException(Exception exception) = UnexpectedLoginException;
}

class LoginSuccess implements LoginResponse {
  final authToken;
  const LoginSuccess(this.authToken);
}

class InvalidLoginCredentials implements LoginResponse {
  const InvalidLoginCredentials();
}

class NoNetworkForLogin implements LoginResponse {
  const NoNetworkForLogin();
}

class UnexpectedLoginException implements LoginResponse {
  final Exception exception;
  const UnexpectedLoginException(this.exception);
}

to how you would write the same in say F#:

type LoginResponse =
  | Success of AuthToken
  | InvalidCredentials
  | NoNetwork
  | UnexpectedException of Exception

(I use the same shorter names as in @ZakTaccardi's original Kotlin example)

You get much better data/domain modelling inside your code when you have tools like this available in the language (@brianegan makes the same argument). It improves readability and quality of the code, not the least because it enables preventing many more illegal states at compile time. And since it is so easy to read and write, you do it right instead of often cheating like you would do in say Dart because you would have to write so much more code to do it "correctly".
Dart is a nice language, and Flutter is a killer. For me, the biggest lack in Flutter is these data modelling capabilities (and pattern matching) of languages of the ML family. (Scala and Kotlin have similar capabilities, but more verbose syntax)
For non-trivial UI I quite often find finite state automata very useful and sum types (and the corresponding pattern matching) is perfect for this.

from language.

LinusU avatar LinusU commented on May 28, 2024 61

I'm really really missing this coming from Swift/iOS to Flutter development.

When dealing with widget/component state, I find it that almost always the state is better described using an ADT rather than several nullable fields. The problem with nullable fields is that it often allows for combinations of values that are simply invalid.

Here is an example from a commercial app I'm currently porting from Swift to Flutter:

class _State {}

class _ChooseProviderState extends _State {}

class _LoadingState extends _State {}

class _ChooseAccountsState extends _State {
  final List<String> accounts;
  final Set<String> selected;

  _ChooseAccountsState(this.accounts) : selected = Set();
}

class _ImportingState extends _State {
  final List<String> imported;
  final List<String> failed;
  final List<String> processing;
  final List<String> queue;

  _ImportingState(this.imported, this.failed, this.processing, this.queue);
}

// ...

  @override
  Widget build(BuildContext context) {
    if (_state is _ChooseProviderState) {
      return _buildChooseProvider(context, _state);
    }

    if (_state is _LoadingState) {
      return _buildLoading(context, _state);
    }

    if (_state is _ChooseAccountsState) {
      return _buildChooseAccounts(context, _state);
    }

    if (_state is _ImportingState) {
      return _buildImporting(context, _state);
    }

    throw Exception('Invalid state');
  }

Apart from being very verbose and cumbersome to type out, there is also no compiler checks that I'm 1) covering all possible states and that 2) there isn't every any other state. This forces me to add a runtime throw in my code.

Now let's compare this to the Swift equivalent:

enum State {
  case chooseProvider,
  case loading,
  case chooseAccounts(accounts: [String], selected: Set<String>),
  case importing(imported: [String], failed: [String], processing: [String], queue: [String]),
}

// ...

  build(context: BuildContext) -> Widget {
    switch state {
    case .chooseProvider:
      return buildChooseProvider(context)
    case .loading:
      return buildLoading(context)
    case .chooseAccounts(let accounts, let selected):
      return buildChooseAccounts(context, accounts, selected)
    case .importing(let imported, let failed, let processing, let queue):
      return buildImporting(context, imported, failed, processing, queue)
    }
  }

Apart from being much much easier to work with, and quickly glance over, it gives me additional compiler guarantees. Here I know that I'm never missing any of the enum cases since the compiler will complain. I also don't have to add a runtime throw, since the compiler can guarantee that execution will never continue after the switch statement.

I would absolutely love to see this in Dart 😍 is there anything I can do to move this forward?

from language.

pulyaevskiy avatar pulyaevskiy commented on May 28, 2024 35

Concise syntax without ==, toString, and hashcode and copy boilerplate, although I think "data classes" are a separate issue.

This is the main source of boilerplate code for me. And this is a lot of boilerplate.

Not sure if ADTs are supposed to address this, but It’s probably number 1 feature in my wishlist.

from language.

munificent avatar munificent commented on May 28, 2024 33

Closing this because records and patterns are now enabled on the main branch of the Dart SDK! 🎉

from language.

derekdreery avatar derekdreery commented on May 28, 2024 30

Apologies if this is a bit of a +1 response, but coming from Rust I really miss ADTs (called enums in rust). Rust's optional Option is vastly superior to everything being nullable - you don't need to annotate @required, the type tells you if it is or not! Rust's Result, which is like Either in Haskell is also a great way to represent fallible operations - quite often the error type is also an ADT with extra data depending on the type of the error. They are also really easy to implement and memory efficient - they are just typed unions from C (although I don't know dart's memory model, maybe everything is behind a pointer anyway).

EDIT To full realise their power you also need some de-structuring support (sometimes called switch/match/case). In rust say you have an ADT like

enum Error { // ADT
    IoError { // 1st variant with named fields
        errno: u32 // unsigned 32-bit int
    }, // 2nd variant with named fields
    ParseError {
        position: usize // unsigned pointer-width int
        msg: String
    },
    UnknownError, // 3rd variant with no fields
}

then the destructuring would look like

match e { // e is an error
    Error::IoError { errno } => {
        // I have access to the fields of the variant here
        // handle the error...
    }
    Error::ParseError { position, msg } => {
        // again I have access to fields
    }
    Error::UnknownError => {
        // nothing to bind here
    }
}

from language.

munificent avatar munificent commented on May 28, 2024 21

The Dart team is focused in NNBD now. All we can now is to thumbs up this issue and keep an eye in here.

Yes, and we do very much appreciate your patience. :) We are also very much looking forward to finishing up NNBD and moving onto other language features.

from language.

satvikpendem avatar satvikpendem commented on May 28, 2024 20

Remi Rousselet, who made the great provider, functional_widget, and flutter_hooks libraries, among others, recently launched the freezed package that includes immutable classes and also algebraic data types, along with an exhaustive when function to switch between the unions.

You can create immutable classes using an abstract class with the @immutable annotation and the specified properties:

@immutable
abstract class Person with _$Person {
  factory Person(String name, int age) = _Person;
}

For ADTs, create an abstract class with factories specifying their values, and use when to pattern match them:

@immutable
abstract class Model with _$Model {
  factory Model.first(String a) = First;
  factory Model.second(int b, bool c) = Second;
}

var model = Model.first('42');

print(
  model.when(
    first: (String a) => 'first $a',
    second: (int b, bool c) => 'second $b $c'
  ),
); // first 42

There's also maybeWhen, similar to other languages where there is a default case, using the orElse keyword:

@immutable
abstract class Union with _$Union {
  const factory Union(int value) = Data;
  const factory Union.loading() = Loading;
  const factory Union.error([String message]) = ErrorDetails;
}

var union = Union(42);

print(
  union.maybeWhen(
    null, // ignore the default case
    loading: () => 'loading',
    // did not specify an `error` callback
    orElse: () => 'fallback',
  ),
); // fallback

It looks to be a pretty cool library. I'm glad that Dart has code generation so that we can create language features via libraries instead of just waiting for them by the official Dart team, although language features would be preferred over libraries of course, if only to cut down the amount of generated code and boilerplate.

from language.

jjoelson avatar jjoelson commented on May 28, 2024 20

I'm not sure if commenting here will be helpful all, but I'm a big +1 to this feature request. When it comes to modeling a problem domain in a modern programming language, I'd say ADTs are very nearly as important as null safety. With null-safety implemented, I can't imagine any language feature that ought to be a higher priority than ADTs. It's similar to null-safety in the sense that you can't properly model your problem domain without it.

Flutter being such an important use of Dart implicitly puts Dart in direct competition with Swift and Kotlin. Both of those languages not only support ADTs, but have a strong developer culture of using them heavily. That means that while there is a lot about the developer experience that improves when one switches from native to Flutter, the lack of ADTs in Dart is a glaring regression that is very noticeable to iOS and Android devs.

from language.

flukejones avatar flukejones commented on May 28, 2024 19

All of these external packages feel like bandaid solutions to something that would likely be able to optimize heavily if it were in the language as a standard feature.

from language.

flukejones avatar flukejones commented on May 28, 2024 16

Chiming in to say that having spent the last few years neck deep in Rust and Swift, coming to Dart without ADT feels a little like having a hand tied behind your back.

This really is a must have language feature and would enable easier creation of robust code.

from language.

ilexborCodexOptimus avatar ilexborCodexOptimus commented on May 28, 2024 14

How long can I wait? To write secure code, you have to suffer. Sealed classes have been implemented in all modern languages for a long time.

from language.

munificent avatar munificent commented on May 28, 2024 12

I want data classes and removing the ;.

These are tracked on separate issues.

And also enum class bring nothing but string and index!

The next stable release of Dart will allow you to define fields, methods, and other members on enum types. It's out in the beta channel now.

from language.

Nikitae57 avatar Nikitae57 commented on May 28, 2024 11

Another vote for this feature

from language.

thosakwe avatar thosakwe commented on May 28, 2024 10

Just adding OCaml's variant types to the conversation here.

A sum type in OCaml looks like:

type FeedWidgetState =
  | Loading
  | Failed of exn * stack_trace
  | Succeeded of tweet list

let build state context =
  match state with
    | Loading -> Flutter.circularProgressIndicator
    | Failed e _ ->
        let errMsg = Printexc.to_string e in
        Flutter.center (Flutter.text errMsg)
    | Succeeded accounts ->
         let render_account account =
           Flutter.listTile ~title: (Flutter.text account.handle)
         in
         Flutter.listView ~children: (List.map render_account accounts)

Obviously, the syntax is pretty different from Dart, but theoretically a translation could still look pretty Dart-y:

sum class FeedWidgetState {
   Loading,
   Failed(Object error),
   Succeeded(List<Account> accounts)
}

Widget build(BuildContext context) {
   // Or: match (await someComputation(2, "3")) as state
   return match state {
     Loading => CircularProgressIndicator(),
     Failed => Center(child: Text(state.error.toString())),
     Succeeded {
       return ListView(
         children: state.accounts.map(renderAccount).toList(),
       );
     }
   }
}

EDIT: It would also be cool to potentially even allow method/field definitions in sum class, though that might be a little too much to ask for...

sum class FeedWidgetState {
   Loading,
   Failed(Object error),
   Succeeded(List<Account> accounts)

   String common() => "hello!"
 
   void doIt() {
      // Do it...
    }

    @override
     String toString() {
        return match this {
           // ...
        }
     }
}

// OR...
class FeedWidgetState {
  sum Foo(), Bar(String x), Baz(List<String> quux)

from language.

nodinosaur avatar nodinosaur commented on May 28, 2024 10

Seems to me that the effort creating a new library might have been better spent improving Sealed Unions as it is already being widely used.
Still at least there is choice :)

from language.

xuan-nguyen-swe avatar xuan-nguyen-swe commented on May 28, 2024 9

I like Flutter, but Dart language in general, especially Enum in Dart, is holding me back :(. Hope that, Dart will soon be comparable with modern languages like Kotlin, Swift or C#.

from language.

tchupp avatar tchupp commented on May 28, 2024 8

ADTs are also known as "tagged unions".

In typescript, you may represent the above example as:

type Success = {
  tag: "Success"
  authToken: string;
};

type InvalidCredentials = {
  tag: "InvalidCredentials"
};

type NoNetwork = {
  tag: "NoNetwork"
};

type UnexpectedException = {
  tag: "UnexpectedException"
  error: Error
};

type LoginResponse =
  Success
  | InvalidCredentials
  | NoNetwork
  | UnexpectedException

The tag property is shared by all the different structural types, which allows us to use a switch statement to identify which type we have.

Option<T> from Rust, which is referenced above, is another example of a tagged union.

There is also the concept of an "untagged union", which could simply refer to having one object with nullable fields represent multiple scenarios:

type Option<T> = {
  isPresent: bool;
  value?: T;
};

Untagged unions can also refer to syntax sugar for dealing with nullable types:

_optionalVar?.foo();

Most languages with nullable types can support untagged unions, but Dart clearly has a special understanding since it supports the safe-access operators.

In addition, dart (and similar style languages) provide smart casting (and "instance of" checking) on exceptions with try/catch statements.

Pure OO practices would discourage the use of ADTs, since it involves "casting" an abstract class to a concrete type after doing an "instance of" check. The OO pattern that would replace the need for ADTs is the visitor pattern, but this adds so much overhead just to stay in the OO space that the benefit is often not worth the cost.
Also these languages usually provide exception type checking, which violates that pattern anyways.

I'm not super familiar with how the dart compiler works, but if we are able to identify all the implementation of an abstract class at compile time, it might be possible to remove the need for the else statement at the end a series of is checks.

from language.

flukejones avatar flukejones commented on May 28, 2024 7

That may be the case but I'm sure a language level solution would be much better than ad-hoc with third-party libraries, and would also expose opportunities for compiler level optimizations. Not to mention that the documentation might be a lot more user friendly rather than a "go read this thing written for another language".

from language.

pedromassango avatar pedromassango commented on May 28, 2024 7

hiya, any updates on ADT support? 🙂

The Dart team is focused in NNBD now. All we can now is to thumbs up this issue and keep an eye in here.

from language.

marcguilera avatar marcguilera commented on May 28, 2024 7

I think the overall experience writing dart is good but I do miss some things and by reading here it looks like I'm not alone. It would be great if we could have the equivalent of:

  • Sealed interfaces.
  • Type matching in switch (or some other keyword like when) which could be exhaustive for sealed interfaces.
  • Data classes or records to represent data with automated copyWith, equals and hashCode. I think the main use case is for immutable data, but maybe it doesn't need to be restricted to it.

Packages like freezed are great but having to rely on code generation for this common cases feels sub optimal in my opinion.

The good news is that it looks like there are issues for most of these and are in various levels of discussions already. It's great we are getting fields in enums in the next release!

from language.

avilladsen avatar avilladsen commented on May 28, 2024 6

Can you be specific about what you're looking for in language-level support that is not currently possible in Dart? Is it the more concise syntax for declaring ADTs? The static checking of when-style statements?

from language.

xsahil03x avatar xsahil03x commented on May 28, 2024 6

I did something similar to what @avilladsen and @renatoathaydes mentioned above and was satisfied with the results. To avoid the boilerplate, I built a code generator with my colleague which generates these classes by annotating Enums.

@superEnum
enum _MoviesResponse {
  @Data(fields: [DataField('movies', Movies)])
  Success,
  @object
  Unauthorized,
  @object
  NoNetwork,
  @Data(fields: [DataField('exception', Exception)])
  UnexpectedException
}

where:-

@Data() marks an enum value to be treated as a Data class.

  • One should supply a list of possible fields inside the annotation.

  • If you don't want to add fields, use @object annotation instead.

  • Fields are supplied in the form of DataField objects.

  • Each DataField must contain the name and the type of the field.

  • If the field type needs to be generic use Generic type and annotate the enum value with @generic annotation.

@object marks an enum value to be treated as an object.

Then it can be used easily with the generated when function

 moviesResponse.when(
    success: (data) => print('Total Movies: ${data.movies.totalPages}'),
    unauthorized: (_) => print('Invalid ApiKey'),
    noNetwork: (_) => print(
      'No Internet, Please check your internet connection',
    ),
    unexpectedException: (error) => print(error.exception),
  );

from language.

tchupp avatar tchupp commented on May 28, 2024 5

@LinusU great example!

Dart has the idea of a refinement/type test/smart cast. I wonder if this could work similar to how Kotlin does this.

To re-use @LinusU's example:

when (_state) {
  is _ChooseProviderState -> ...
  is _LoadingState -> ...
  is _ChooseAccountsState -> ...
  is _ImportingState -> ...
}

Kotlin was originally designed on top of the JVM.
Java doesn't have the idea of smart casting, so this was a significant improvement:

if (_state instanceof _ChooseProviderState) {
  _ChooseProviderState inner = (_ChooseProviderState) _state;
  ...
}
...etc.

Dart already has the idea of smart casting, so I have a feeling that implementing this should not be the most difficult. Agreeing on a syntax will be the fun part 😅

(To steal what @LinusU said) Is there anything I could do to make this possible?

from language.

renatoathaydes avatar renatoathaydes commented on May 28, 2024 5

@LinusU @tchupp I agree that native support for ADTs would be nice and that it's much easier to work with ADTs in Rust/Kotlin/Swift/F# etc!

But at least the problem of not having compiler guarantees can be mostly worked around in Dart.

To continue on the example @LinusU posted above, you can add a method to the common type, _State, which lets you use it in a type-safe manner, ensuring you don't forget any case, by moving the if/else chain into a single place (a technique I use in Java normally, but works in Dart too - as long as you keep constructors for the sub-types in the hierarchy private, so they can only be instantiated via the common type via static methods, or within the same file):

abstract class _State {
  T use<T>(
      T Function(_ChooseProviderState) useChooseProviderState,
      T Function(_LoadingState) useLoadingState,
      T Function(_ChooseAccountsState) useChooseAccountState,
      T Function(_ImportingState) useImportingState) {
    if (this is _ChooseProviderState) {
      return useChooseProviderState(this);
    }
    if (this is _LoadingState) {
      return useLoadingState(this);
    }
    if (this is _ChooseAccountsState) {
      return useChooseAccountState(this);
    }
    if (this is _ImportingState) {
      return useImportingState(this);
    }
    throw Exception('Invalid state');
  }
}

Now, your build method looks much nicer:

  @override
  Widget build(BuildContext context) {
    return _state.use(
        (chooseProvider) => _buildChooseProvider(context, chooseProvider),
        (loading) => _buildLoading(context, loading),
        (chooseAccounts) => _buildChooseAccounts(context, chooseAccounts),
        (importing) => _buildImporting(context, importing));
  }

Dart Pad Full Example

from language.

jarlestabell avatar jarlestabell commented on May 28, 2024 4

@i-schuetz I would say you are correct in that there is a deep conceptual difference. In a class (or struct or record), you have the "multiplication" construct of ADTs. What is lacking in Dart at the moment is the "sum" construct of ADTs. I think the sum is better viewed as something different than a special case of inheritance in OOP, although it can to some extent be "simulated" that way.

Here is a bit old, but still very good article about ADTs:
http://static.v25media.com/the-algebra-of-data-and-the-calculus-of-mutation.html

When you get used to sum types, it feels very limited not having them.
For me, TypeScript's tagged unions are quite lacking because pattern matching is missing, which gives you sort of only the first half of the power/convenience of sum types.

from language.

nodinosaur avatar nodinosaur commented on May 28, 2024 3

Yes, I agree that we are missing when in the example I have, the _when method is not exhaustive, nor does it catch any additional sub-types added to the parent class with the @sealed annotation.
For example, adding an isPaused does not give any IDE or Compiler hints:

class IsPaused extends ViewState {
  @override
  ViewState reduce(ViewState state) {
    return _when(
      (isIdle) => print('isIdle'),
      (isloading) => print('isloading'),
      (success) => print('${success.data}'),
      (error) => print('${error.message}'),
    );
  }
}

What I would like to see if there is an official Dart example of how we ought to be implementing @sealed? (cc. @srawlins \ @mit-mit)

from language.

Abion47 avatar Abion47 commented on May 28, 2024 3

@adrianboyko Check again under "Patterns and other features".

from language.

ds84182 avatar ds84182 commented on May 28, 2024 2

Looks great! I'd suggest using @required named parameters though, so you don't have to depend on a particular argument order. It also makes it possible to remove subtypes in the future without getting... lots of unreadable errors from the analyzer.

from language.

renatoathaydes avatar renatoathaydes commented on May 28, 2024 2

Everyone wants to solve this problem :D here's a few more existing libraries from #546 (comment)

If existing packages are interesting to see, here's a bunch of them:

from language.

pedromassango avatar pedromassango commented on May 28, 2024 2

Where this is being worked on or tracked?

We are VERY much heads-down on null-safety at the moment. If you're subscribed to this issue, you'll see updates as they happen.

Thank you. I really want to stay up to date with this.

from language.

He-Pin avatar He-Pin commented on May 28, 2024 2

I prefer we take a look at the Scala 3:
https://docs.scala-lang.org/tour/case-classes.html
https://docs.scala-lang.org/tour/pattern-matching.html
https://docs.scala-lang.org/overviews/core/value-classes.html#inner-main
https://docs.scala-lang.org/scala3/book/types-union.html
https://docs.scala-lang.org/scala3/book/types-intersection.html
https://docs.scala-lang.org/scala3/reference/enums/enums.html
https://docs.scala-lang.org/scala3/book/types-opaque-types.html#inner-main

from language.

Abion47 avatar Abion47 commented on May 28, 2024 2

@hepin1989

  • Case classes:
  • Pattern matching:
  • Value classes:
    • In a way, Dart already has this with const constructors.
  • Types union:
    • This issue, #83
  • Types intersection:
    • This issue and maybe #83 as well, or maybe it's out of scope there.
  • Enums:
    • A lot of issues tracking enum improvements, did you have something specific in mind?
  • Opaque types:
    • This can be done today with typedefs and extension methods.

from language.

renatoathaydes avatar renatoathaydes commented on May 28, 2024 1

Very cool @xsahil03x ... the existing solutions to this are probably not as nice.

https://github.com/google/built_value.dart/blob/master/example/lib/enums.dart

https://github.com/werediver/sum_types.dart

Would be nice if one alternative became the standard though.

from language.

nodinosaur avatar nodinosaur commented on May 28, 2024 1

True, but as Brian mentioned we have had sealed_unions for nearly 2 years now and other have managed to use them successfully during that time.
However, I am glad to see that the community at large is helping the language.

from language.

veggiedefender avatar veggiedefender commented on May 28, 2024 1

hiya, any updates on ADT support? 🙂

from language.

nodinosaur avatar nodinosaur commented on May 28, 2024 1

I tend to use https://pub.dev/packages/dartz for functional programming types.

Functional programming in Dart

Type class hierarchy in the spirit of cats, scalaz and the standard Haskell libraries
• Immutable, persistent collections, including IVector, IList, IMap, IHashMap, ISet and AVLTree
• Option, Either, State, Tuple, Free, Lens and other tools for programming in a functional style
• Evaluation, a Reader+Writer+State+Either+Future swiss army knife monad
• Type class instances (Monoids, Traversable Functors, Monads and so on) for included types, as well as for several standard Dart types
• Conveyor, an implementation of pure functional streaming

from language.

JarvanMo avatar JarvanMo commented on May 28, 2024 1

This is really awesome for Kotlin developers. You can say the current built-in types are enough for us but ENOUGH is far less than what we need. As a newer language, I believe we can do better in grammar sugar. I miss Kotlin very single time I write Flutter! I want data classes and removing the ;. And also enum class bring nothing but string and index! I can't say it can not work but I have to say I have to write more branches.

from language.

lrhn avatar lrhn commented on May 28, 2024

Very similar to http://github.com/dart-lang/sdk/issues/31253.

from language.

owlmafia avatar owlmafia commented on May 28, 2024

@lrhn There may be similarities in the way in which it will be implemented in Dart, but other than that this is unrelated with data classes.

from language.

owlmafia avatar owlmafia commented on May 28, 2024

@tchupp Is visitor pattern really a replacement? Doesn't it just move the "instance of" checks to the visitor (I may be mistaken, not super familiar with it)?

Also, I wonder whether ADTs are just convenience for inheritance in OOP, or there's a deeper conceptual difference that makes the "instance of" not violate OOP practices in this case. It feels like may be a fundamental difference between if you're expecting Animal, which can be a Cat or Dog or Result, which can be Ok or Error. A manifestation of this may be that in most languages you can't share data between the types in ADTs, while using inheritance you can. Not sure about this take.

Another take could be that the the "ADT" is just convenience to do do instance of checks in a type safe and concise way. Making the ifs exhaustive, in combination with smart casting, would be no different than just having "ADT"s, except with an uglier syntax.

from language.

Aetet avatar Aetet commented on May 28, 2024

Hey @kevmoo any plans on this?

from language.

kevmoo avatar kevmoo commented on May 28, 2024

CC @munificent

from language.

nodinosaur avatar nodinosaur commented on May 28, 2024

@sealed annotation is now available as of meta ^1.1.7
merged in on #11 => meta.dart#L202

from language.

nodinosaur avatar nodinosaur commented on May 28, 2024

How does this example look?
(Comments, correct alternative implementations welcomed)

https://gist.github.com/nodinosaur/adf4da0b5f6cddbdcac51c271db56465

from language.

andrewackerman avatar andrewackerman commented on May 28, 2024

ADTs as a whole can mostly be represented in dart currently using inheritance. The only thing that's limiting those approaches' success is their verbosity. However, I think this would largely be alleviated by the addition of data classes and the addition of a when construct which can be an extension of a switch block.

Take the Kotlin code in the OP, for example. If Dart had data classes and a when construct, the Dart equivalent of the Kotlin code might look something like this:

abstract data class LoginResponse;
data class Success(String authToken) extends LoginResponse;
data class InvalidCredentials extends LoginResponse;
data class NoNetwork extends LoginResponse;
data class Unexpected(Error error) extends LoginResponse;

// ...

on(LoginResponse loginResponse) {
  when (loginResponse) {
    is Success:
      // direct access to auth token available w/o need to cast
      return loginResponse.authToken;
    is InvalidCredentials:
      return null;
    is NoNetwork:
      return null;
    is Unexpected:
      return loginResponse.error;
  }
}

If Dart gets support for pattern matching in switch blocks, this might not even need a dedicated when construct as the type matching can be made in the case statements.

The only thing this doesn't cover is the exhaustive type matching, but IMO I'm not sure that's a bad thing. Having exhaustive type matching is nice for type safety or testing, but it also cuts into the ability of a type being extensible.

from language.

andrewackerman avatar andrewackerman commented on May 28, 2024

@sealed just means the class isn't supposed to be extensible, i.e. it can't be the superclass to any other class. The ADT behavior of sealed classes in Kotlin just takes advantage of the fact that sealed classes in Kotlin can have child classes made, but they must be in the same file as the sealed class. (So, in reality, Kotlin is also using inheritance to achieve ADT-like behavior. There are just a couple more features Kotlin offers [like exhaustive type checking] to make these "ADTs" more tidy to use overall.)

from language.

tvh avatar tvh commented on May 28, 2024

Another alternative is https://github.com/factisresearch/sum_data_types. It offers both sum- and product-types. Unfortulately the Syntax for the constructors is a bit cumbersome.

from language.

nodinosaur avatar nodinosaur commented on May 28, 2024

Yes, ultimately that is what we all want & hopefully we won't be waiting too much longer :)

from language.

pedromassango avatar pedromassango commented on May 28, 2024

Where this is being worked on or tracked?

from language.

kevmoo avatar kevmoo commented on May 28, 2024

Where this is being worked on or tracked?

We are VERY much heads-down on null-safety at the moment. If you're subscribed to this issue, you'll see updates as they happen.

from language.

jodinathan avatar jodinathan commented on May 28, 2024

well, I guess technically Dart has some of this implemented on try catch clause.

I guess what we can do now is use NNBD and feedback them so it can go stable soon.

from language.

Producer86 avatar Producer86 commented on May 28, 2024

for the time being, a visitor can also be used to get compile-time errors about missing cases.

abstract class ResponseVisitor<R> {
  R visitSuccess(_SuccessResponse response);
  R visitFailure(_FailureResponse response);
}

abstract class _Response {
  R accept<R>(ResponseVisitor<R> visitor);
}

class _SuccessResponse implements _Response {
  _SuccessResponse(this.data);

  final int data;

  @override
  R accept<R>(ResponseVisitor<R> visitor) {
    return visitor.visitSuccess(this);
  }
}

class _FailureResponse implements _Response {
  _FailureResponse(this.message);

  final String message;

  @override
  R accept<R>(ResponseVisitor<R> visitor) {
    return visitor.visitFailure(this);
  }
}

class ServiceClass {
  _Response _fetchData() {
    try {
      return _SuccessResponse(42);
    } on Exception {
      return _FailureResponse('No data found');
    }
  }

  void getData(ResponseVisitor visitor) {
    _fetchData().accept(visitor);
  }
}

class WorkerClass implements ResponseVisitor<void> {
  WorkerClass(this.service);

  final ServiceClass service;

  @override
  void visitFailure(_FailureResponse response) {
    print('Error happened: ${response.message}');
  }

  @override
  void visitSuccess(_SuccessResponse response) {
    print('Result arrived: ${response.data}');
  }

  void doWork() {
    service.getData(this);
  }
}

void main() {
  final service = ServiceClass();
  WorkerClass(service).doWork();
}

from language.

maks avatar maks commented on May 28, 2024

Thanks @munificent I'm certainly very much appreciate all the efforts the Dart team is putting in for NNBD! And NNBD already makes the "enums+extensions with switch's" pattern so much more useful for the kind of things that I'm used to using sealed classes.

And in the meantime I spotted some new lints in Dart 2.9 that I think might help with some of the uses described here until there is built in language support: exhaustive_cases and no_default_cases.

from language.

pedromassango avatar pedromassango commented on May 28, 2024

Related to #83

from language.

bubnenkoff avatar bubnenkoff commented on May 28, 2024

Does Algebraic DT include monads like Either?

from language.

minikin avatar minikin commented on May 28, 2024

@bubnenkoff check this pub https://pub.dev/packages/result_type it might help.

from language.

bubnenkoff avatar bubnenkoff commented on May 28, 2024

from language.

adrianboyko avatar adrianboyko commented on May 28, 2024

Sum types are not yet in the language funnel :(

from language.

renatoathaydes avatar renatoathaydes commented on May 28, 2024

It's great we are getting fields in enums in the next release!

That was released in Dart 2.17 a few days ago. See the announcement: https://medium.com/dartlang/dart-2-17-b216bfc80c5d

from language.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.