Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Add method annotations: primitive:nil? and primitive:not_nil? #905

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

tk0miya
Copy link
Contributor

@tk0miya tk0miya commented Sep 1, 2023

To represent methods like Object#blank? and Object#present? in ActiveSupport, this adds method annotations primitive:nil? and primitive:not_nil?.

They will be considered as same as nil? and not nil? internally.

refs: #472

Please let me know your opinion about implementation, the naming of annotations, and so on.

To represent methods like `Object#blank?` and `Object#present?` in
ActiveSupport, this adds method annotations `primitive:nil?` and
`primitive:not_nil?`.

They will be considered as same as `nil?` and not `nil?` internally.

refs: soutaro#472
@soutaro soutaro added this to the Steep 1.6 milestone Oct 13, 2023
@soutaro soutaro removed this from the Steep 1.6 milestone Oct 27, 2023
@ParadoxV5
Copy link
Contributor

Someone in Ruby Discord asked if we can write “truthy”, i.e. exact opposite of false | nil. Would that in the RBS core be more preferred?

@tk0miya
Copy link
Contributor Author

tk0miya commented Oct 29, 2023

The goal of this PR is to provide type narrowing to Steep. So this is not related to the "truthy" type that you mentioned.

For example, the following code is available in Ruby on Rails:

name = params[:name]  #=> String?
if name.present?
  name  #=> String
end

But current steep does not support this type narrowing.

This PR adds a new feature to define type narrowing methods via primitive:nil? and `primitive:not_nil? annotation.

@derikson
Copy link

derikson commented Mar 4, 2024

If we defined #present? as:

class Object
  %a{pure} def present?: () -> bool
end

class NilClass
  %a{pure} def present?: () -> ::FalseClass
end

In the example below, shouldn't Steep have enough information to narrow name from String? to String inside the conditional?

name = params[:name]  #=> String?
if name.present?
  name  #=> String
end

This doesn't appear to work currently, but it seems like it should.

def test_logic_receiver_is_nil_via_annotation
with_checker(<<-RBS) do |checker|
class Object
%a{primitive:nil?}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike nil?, blank? doesn't guarantee the receiver is nil (e.g. it returns true for empty string)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants