I am looking for various approaches for supporting some level of intellisense on a dynamically typed language. Since intellisense information is based on type information, there are inherent difficulties in implementing this for dynamic languages.
Do you know any algorithms or methods to implement it?
You need to write an abstract interpreter that executes the code with type values. So you step with your abstract interpreter trough the AST and record for each variable the sent messages or known types. And when you are done, you infer the possible types using structural type equivalence (aka duck typing).
PS: in addition to type inference you might want to take a look at "How Program History Can Improve Code Completion" by Romain Robbes, is explains how to further improve auto completion in dynamic languages with most-recently-used information and collaborative filtering.
So here is how abstract interpretation works for a code snippet like
def groups(array,&block)
groups = Hash.new
array.each { |ea|
key = block.call(ea)
groups[key] = [] unless groups.include? key
groups[key] << ea
}
return groups
end
you would start with
array = { :messages => [], :types => [] }
block = { :messages => [], :types => [] }
and then
array = { :messages => [], :types => [] }
block = { :messages => [], :types => [] }
groups = { :messages => [], :types => [Hash] }
and then
array = { :messages => [:each], :types => [] }
block = { :messages => [], :types => [] }
groups = { :messages => [], :types => [Hash] }
and then
array = { :messages => [:each], :types => [] }
block = { :messages => [:call], :types => [] }
groups = { :messages => [], :types => [Hash] }
key = { :messages => [], :types => [] }
and then
array = { :messages => [:each], :types => [] }
block = { :messages => [:call], :types => [] }
groups = { :messages => [:include?,:[]], :types => [Hash] }
group_elements = { :messages => [], :types => [Array] }
key = { :messages => [], :types => [] }
and then
array = { :messages => [:each], :types => [] }
block = { :messages => [:call], :types => [] }
groups = { :messages => [:include?,:[]], :types => [Hash] }
group_elements = { :messages => [:<<], :types => [Array] }
key = { :messages => [], :types => [] }
so eventually we can infer that
array is possibly an Enumerable
block is possibly a Proc
groups is a Hash with Array elements
key is any object
I would download the sources of the Groovy plugin for eclipse, it has intellisense (as much as possible), and think Groovy is a good sample of a dyanamic language with dynamic typing
Easy, just one additional step should be added - type inference. After that you know type information and can suggest something to the user.
Note that a "dynamic language" and a "dynamically typed language" are not necessarily the same thing.
The way that Microsoft handles this in the intellisense for Javascript (VS2008) is that it infers, to the best of its ability, what type the var currently holds. If/when this changes, following references to the var will present options for the updated type.
Related
I am still very new to rust, coming from a C embedded world.
If i have a piece of code like this:
match self {
Command::AT => String::from("AT"),
Command::GetManufacturerId => String::from("AT+CGMI"),
Command::GetModelId => String::from("AT+CGMM"),
Command::GetFWVersion => String::from("AT+CGMR"),
Command::GetSerialNum => String::from("AT+CGSN"),
Command::GetId => String::from("ATI9"),
Command::SetGreetingText { ref enable, ref text } => {
if *enable {
if text.len() > 49 {
// TODO: Error!
}
write!(buffer, "AT+CSGT={},{}", *enable as u8, text).unwrap();
} else {
write!(buffer, "AT+CSGT={}", *enable as u8).unwrap();
}
buffer
},
Command::GetGreetingText => String::from("AT+CSGT?"),
Command::Store => String::from("AT&W0"),
Command::ResetDefault => String::from("ATZ0"),
Command::ResetFactory => String::from("AT+UFACTORY"),
Command::SetDTR { ref value } => {
write!(buffer, "AT&D{}", *value as u8).unwrap();
buffer
},
Command::SetDSR { ref value } => {
write!(buffer, "AT&S{}", *value as u8).unwrap();
buffer
},
Command::SetEcho { ref enable } => {
write!(buffer, "ATE{}", *enable as u8).unwrap();
buffer
},
Command::GetEcho => String::from("ATE?"),
Command::SetEscape { ref esc_char } => {
write!(buffer, "ATS2={}", esc_char).unwrap();
buffer
},
Command::GetEscape => String::from("ATS2?"),
Command::SetTermination { ref line_term } => {
write!(buffer, "ATS3={}", line_term).unwrap();
buffer
}
}
How does it work in Rust? Will all these match arms evaluate immediately, or will only the one matching create a mutable copy on the stack? And also, will all the string literals withing my String::from("") be allocated in .rodata?
Is there a better way of doing what i am trying to do here? Essentially i want to return a string literal, with replaced parameters (the write! macro bits)?
Best regards
Only the matching arm will be evaluated. The non matching arms have no cost apart the size of the program.
In the general case, it's not even possible to evaluate other arms, as they depend on data read using destructuring of the pattern.
As for your second question, the location in a program where literals are stored isn't commonly named rodata, and it's neither specified nor guaranteed (it's usually deduplicated but that's just optimization).
I want to match several function results without calling all of them if it is not necessary:
fn foo() -> bool {
println!("foo executed");
true
}
// I want to do something like this
// but there is a redundant function call
match (foo(), foo()) {
(false, true) => println!("Bingo!"),
_ => println!("Wrong result"),
}
// No redundant function call
// but less impressive and doubling of wrong result processing
match foo() {
false => match foo() {
true => println!("Bingo"),
_ => println!("Wrong result"),
},
_ => println!("Wrong result"),
}
How can I do this?
You can simply do:
if !foo() && foo() { println!("Bingo") } else { println!("Wrong result") }
The "and" (&&) and "or" (||) logical operators in Rust are short-circuiting, like in most languages.
Since !foo() is false, the right side of the && will not be evaluated and foo() will not be called a second time.
Your macro solution is basically re-inventing short-circuiting, at least for this toy example (maybe it becomes more readable with your actual code...).
I found out that I could beautify the second approach with macro:
macro_rules! lazy_match {
(($condition:expr => $expect:pat) => $on_expected:expr, _ => $other:expr) => (
match $condition {
$expect => $on_expected,
_ => $other,
}
);
(
($condition:expr => $expect:pat, $($c:expr => $e:pat),+)
=> $on_expected:expr, _ => $other:expr
) => (
match $condition {
$expect => lazy_match!(($($c => $e),+) => $on_expected, _ => $other),
_ => $other,
}
);
}
lazy_match! {
(foo() => false, foo() => true) => println!("Bingo"),
_ => println!("Wrong result")
};
I have one custom field in Logstash event defined as expression:
{ "customIndex" => "my-service-%{+YYYY.MM}" }
And filter that calculates index name for elasticsearch output plugin:
filter {
if [customIndex] {
mutate {
add_field => { "indexName" => "custom-%{customIndex}" }
}
} else {
mutate {
add_field => { "indexName" => "common-%{+YYYY.MM.dd}" }
}
}
}
But for custom index it creates invalid name custom-my-service-%{+YYYY.MM} and does not evaluate %{+YYYY.MM} expression.
Is it possible to evaluate field and get custom-my-service-2016.11?
If you can reformat your created field to this:
{ "customIndex" => "my-service-%Y.%m" }
Then this Ruby filter will do the trick:
ruby {
init => "require 'date'"
code => "event['indexName'] = 'custom-' + Date.today.strftime(event['customIndex'])"
}
Here is a documentation on placeholders you can use.
Trying to do something like this:
# nodes.pp
node 'dev-a-1.sn1.vpc1.example.com' inherits project_j1n_sn1_vpc1_dev {
class { 'custom::core':
overrides => {
'openssh' => {'settings' => {'external_access' => 'true'}}, # Allow direct mounting for dev
'rsyslog' => {'settings' => {'role' => 'node', 'filters' => {'php' => {'target' => 'remote'}, 'mail' => {'target' => 'remote'}}}}
}
}
}
# custom::core
class custom::core($overrides = {}) {
if (has_key($overrides, 'openssh')) {
$settings = $overrides['openssh']['settings']
# Doesn't work
create_resources('openssh', $settings)
# Doesn't work
class { 'openssh': $settings }
}
}
Is it possible to call a class and pass a hash as the arguments?
Puppet/Puppetmaster v2.7.26-1 (Centos 6.7)
There is a way in Puppet 4+.
class { 'ssh':
* => $settings
}
Learn all about it on roidelapluie's blog.
A co-worker of mine had come up with a good solution to something similar in the old 2+ days of puppet. Utilized create_resources for this.
http://www.followski.com/quick-note-how-to-use-create_resources-to-initialize-a-class-in-puppet/
your code can look something like this:
nodes.pp
node 'dev-a-1.sn1.vpc1.example.com' inherits project_j1n_sn1_vpc1_dev {
class { 'custom::core':
overrides => {
'openssh' => {'settings' => {'external_access' => 'true'}}, # Allow direct mounting for dev
'rsyslog' => {'settings' => {'role' => 'node', 'filters' => {'php' => {'target' => 'remote'}, 'mail' => {'target' => 'remote'}}}}
}
}
}
custom::core
class custom::core($overrides = {}) {
if (has_key($overrides, 'openssh')) {
$settings = $overrides['openssh']['settings']
create_resources('class', { 'openssh' => $settings })
}
}
You will notice that in the example linked above it looks like create_resources('class', $params), but that assuming you have a hash with a key being the class name (ie openssh) and its value being the params to set. The example I state above essentially does the same thing.
Your node definition can also be look like this:
node 'dev-a-1.sn1.vpc1.example.com' inherits project_j1n_sn1_vpc1_dev {
class { 'custom::core':
overrides => {
'openssh' => {'external_access' => 'true'}, # Allow direct mounting for dev
'rsyslog' => {'role' => 'node', 'filters' => {'php' => {'target' => 'remote'}, 'mail' => {'target' => 'remote'}}}
}
}
}
and your class something like this:
class custom::core($overrides = {}) {
if (has_key($overrides, 'openssh')) {
create_resources('class', {'openssh' => $overrides['openssh'])
}
if (has_key($overrides, 'rsyslog')) {
create_resources('class', {'rsyslog' => $overrides['rsyslog'])
}
}
I think you get the point by now.
We used this quite often a few years ago. You may want to consider moving towards an upgrade of your puppet infrastructure however.
Hope this helps, or at least points you in the right direction.
I have a running manifest, where I create a folder and a file from a setting (exerpt):
define ffnord::mesh(
$mesh_if_id = "low",
$mesh_mtu_low = 1280,
$fastd_low_port = 11280, # fastd port
) {
ffnord::fastd { "fastd_${mesh_code}":
mesh_if_id => $mesh_if_id,
mesh_mtu_low => $mesh_mtu_low,
fastd_low_port => $fastd_low_port,
}
}
and
define ffnord::fastd( $mesh_if_id
, $mesh_code
, $mesh_mtu_low = 1280
, $fastd_low_port
) {
file {
"/etc/fastd/${mesh_code}-mesh-low-vpn/":
ensure =>directory,
require => Package[ffnord::resources::fastd];
"/etc/fastd/${mesh_code}-mesh-low-vpn/fastd.conf":
ensure => file,
notify => Service[ffnord::resources::fastd],
content => template('ffnord/etc/fastd/fastd-low.conf.erb');
}
}
How can I define a variable amount of those configs:
$mesh_if_id = "low",
$mesh_mtu_low = 1280,
$fastd_low_port = 11280, # fastd port
$mesh_if_id = "something",
$mesh_mtu_low = 12345,
$fastd_low_port = 112345, # fastd port
...
and loop through those blocks to create a folder and file inside ffnord/etc/fastd/ for each block automatically?
(I want to solve this problem: https://github.com/ffnord/ffnord-puppet-gateway/pull/116#issuecomment-100619610 )
In Puppet 3.x there is no "looping", but there are a few tricks. You can pass a Hash of data that represents N number of ffnord::fastd instances:
define define ffnord::mesh($fastd_hash) {
create_resources('ffnord::fastd', $fastd_hash)
}
define ffnord::fastd($mesh_code, $fastd_low_port, $mesh_mtu_low = 1280) {
file {
"/etc/fastd/${mesh_code}-mesh-low-vpn/":
ensure =>directory,
require => Package[ffnord::resources::fastd];
"/etc/fastd/${mesh_code}-mesh-low-vpn/fastd.conf":
ensure => file,
notify => Service[ffnord::resources::fastd],
content => template('ffnord/etc/fastd/fastd-low.conf.erb');
}
}
$hash_of_fastds = {
"low_id" => {
mesh_code => 'low,
mesh_mtu_low => 1280,
fastd_low_port => 11280,
},
"some_id" => {
mesh_code => 'something',
mesh_mtu_low => 12345,
fastd_low_port => 112345,
},
}
ffnord::mesh { 'foo': fastd_hash => $hash_of_fastds, }
Note I've modified define ffnord::fastd slightly, where you had a $mesh_if_id parameter I've turned this into the $namevar of ffnord::fastd.
The first level of $hash_of_fastds translates to the names of the ffnord::fastd instances, the second level of the hash are the parameters for each ffnord::fastd.
See the documentation on the create_resources function for more information.
In Puppet 4 you could use the each function to achieve a similar result.